table <- read.csv2("ssa.csv")
long <- table$COL1
short <- na.omit(table$COL7)

Long series

plot(long, type='l')

spec.pgram(long, detrend = TRUE, fast = FALSE, log='no', taper=0)

Предполагем, что достаточно сгруппировать 8 компонент, т.к. возможно, ранг равен 8 (линейный тренд + 3 косинуса).

Как выбирать L

Так как точной разделимости в реальных рядах не бывает, нас интересует асимтотическая разделимость.

В большинстве случаев скорость асимптотической разделимиости пропорциональна \(1/\min(L, K)\), \(K = N - L + 1\), поэтому выбираем \(L\) близкое к \(N/2\).

Если мы хотим отделять косинус с некоторым периодом, то для улучшения разделимости рекомендуется брать окно кратное периоду (следует из условий точной слабой разделимости).

Слабая и сильная разделимость

  • Слабая разделимость – ортогональность траекторных подпространств
  • Сильная азделимость – слабая разделимость + в разных компонентах (SVD) разные собственные числа

Decomposition

ssa.long <- ssa(long, kind='1d-ssa')

Собственные числа:

\(\lambda_i = \|\mathbf{X}_i\|^2_{\mathcal{F}}\), \(\lambda_i/ \|\mathbf{X}\|^2_{\mathcal{F}}\) – вклад компоненты

plot(ssa.long)

Как идентифицировать тренд?

Хотим выделить все элементарные компоненты, которые имеют медленно меняющиеся собственные вектора.

Собственные ветора образуют базис траекторного подпространства, следовательно, они имеют вид соответствующих компонент ряда.

Собственные вектора:

plot(ssa.long, type='vectors', idx = 1:15)

Первые два – линейный тренд, дальше идут периодические компоненты.

Как идентифицировать периодичность

Посмотрим на скаттерплоты пар собственных векторов.

  • Знаем, что экспоненциально модулированный косинус соответствует двум собственным тройкам с примерно одинаковыми собственными числами (\(Lw\) – целые). Отсюда следует, что эти SVD компоненты скорее всего идут друг за другом
  • Эти две периодические компоненты (собственные вектора), отличаются на \(\pi/2\) по фазе (при целых \(Lw\)).

Таким образом, видимые Т-угольники соответствуют периодическим компонентам с периодом Т.

plot(ssa.long, type='paired', idx= c(1:20))

Заметны компоненты с периодами 20, 10 и 6. Есть компонента, похожая на период 2.4, компонента похожая на период ~6.

Элементарные восстановленные компоненты

plot(ssa.long, type='series', groups = list(1:2, 3:4, 5:6, 7:8, 9:10, 11:12))

Оценка параметров (ESPRIT)

\(x_n = \sum\limits_{j = 1}^r C_j \mu_j^n + \epsilon_n\) – ряд, \(\mu_j = \rho_je^{2\pi i \omega_j}\) – корни х.п.

Алгоритм ESPRIT-LS:

  • Применив SSA и получив SVD разложение, составляем r собственных векторов в матрицу \(\mathbb{U}\)
  • \(\widehat{\mathbb{D}} = \underline{\mathbb{U}}^-\bar{\mathbb{U}}\)
  • Собственные чилса – оценки сигнальных корней
parestimate(ssa.long, groups = list(1:12), method = "esprit-ls")
   period     rate   |    Mod     Arg  |     Re        Im
   10.008   0.000360 |  1.00036   0.63 |  0.80960   0.58760
  -10.008   0.000360 |  1.00036  -0.63 |  0.80960  -0.58760
   20.006   0.000299 |  1.00030   0.31 |  0.95137   0.30903
  -20.006   0.000299 |  1.00030  -0.31 |  0.95137  -0.30903
      Inf   0.000104 |  1.00010   0.00 |  1.00010   0.00000
      Inf  -0.000109 |  0.99989   0.00 |  0.99989   0.00000
    5.999  -0.000352 |  0.99965   1.05 |  0.49963   0.86583
   -5.999  -0.000352 |  0.99965  -1.05 |  0.49963  -0.86583
    2.217  -0.000564 |  0.99944   2.83 | -0.95263   0.30227
   -2.217  -0.000564 |  0.99944  -2.83 | -0.95263  -0.30227
    6.665  -0.001292 |  0.99871   0.94 |  0.58685   0.80810
   -6.665  -0.001292 |  0.99871  -0.94 |  0.58685  -0.80810

Компоненты смешались. Как понять, это слабая или сильная разделимость?

Нет слабой разделимости – отсутствие ортогональности траекторных подпространств.

  • Посмотреть на матрицу взвешенных корреляций \(\rho_{ij}(X_N^{(i)},X_N^{(j)}) = \frac{(X^{(i)}_N, X^{(j)}_N)_w}{\|X^{(i)}_N\|_w\|X^{(j)}_N\|_w}\).

По такой матрице поймем, хорошо ли мы разделили компоненты. Поймем, какие компоненты делить не стоит.

  • Можно посмотреть на частотные коэффициенты корреляции (достаточное условие слабой отделимости)

W-корреляционная матрица

plot(wcor(ssa.long, groups = as.list(1:15)))

Матрица практически диагональная – тренды и сезонные компоненты практически w-ортогональны

Нет сильной разделимости – одинаковые собственные числа оказались в разных компонентах

  • Можно посмотреть на вклады компонент, ступеньки будут соответствовать близким собственным числам

Reconstruction

В предположении, что ранг ряда 8, получим

long.reconstructed <- reconstruct(ssa.long, list(
  trend = c(1, 2),
  periodic = 3:8))
res.long <- residuals(long.reconstructed)
par(mfrow=c(4,1))
plot(long, type = 'l')
plot(long.reconstructed$trend, type='l', ylab = "trend")
plot(long.reconstructed$periodic, type='l', ylab = "periodic")
plot(res.long, type='l')

Автоковариационная функция

acf(res.long, lag.max = 50)

Шум похож на белый.

Корни ХП

Min-norm ЛРФ

  • \(P_i\) – столбцы траекторной матрицы
  • \(\underline{P}_{i}\) – вектор \(P_i\) без последней координаты
  • \(\pi_i\) – последняя координата \(P_i\)

Коэффициенты min-norm ЛРФ:

\(\mathcal{R} = \frac{1}{1-\nu^2}\sum\limits_{i=1}^{d}{\pi_i \underline{P}_{i}}\), где \(\nu^2 = \pi_1^2 + … + \pi_d^2\)

\(x_{i + d} = \sum\limits_{k = 1}^da_kx_{i + d - k}\), \(a_d \neq 0\) – минимальная ЛРФ

\(P(\mu) = \mu^d - \sum\limits_{k = 1}^da_k\mu^{d - k}\) – характеристический полином ЛРФ

\(\{\mu_j\}_{j = 1}^p\) – корни х.п. кратности \(k_j\), \(\sum k_j = d\), тогда

\(x_n = \sum\limits_{m = 1}^p(\sum\limits_{j = 0}^{km - 1} C_{m_j} n^j)\mu_m^n\)

min-norm ЛРФ и ее корни (предполагаем, что ранг ряда 8)

lr <- lrr(ssa.long, groups = list(1:8))
plot(lr)

r <- roots(lr)

Хотим построить формулу для сигнала ряда с шумом.

Взяв большое окно и построив min-norm ЛРФ, найдем много корней х.п.

Как выбрать из них те r корней, что определяют структуру сигнала?

Найдем сигнальные корни, воспользовавшись утверждением, что у min-norm ЛРФ все лишние корни по модулю меньше единицы.

mods <- Mod(r)
print(r[mods >= 1])
[1] 0.8098255+0.5875445i 0.8098255-0.5875445i 0.9513031+0.3091901i 0.9513031-0.3091901i 1.0000664+0.0000000i

Перевернем ряд

lr.back <- lrr(ssa.long, groups = list(1:8), reverse = TRUE)
r.back <- roots(lr.back)
mods.back <- Mod(r.back)
print(r.back[mods.back >= 1])
[1] 1.000001+0i

Модули главных корней

main.roots <- c(r[Mod(r) >= 1], 1/r.back[Mod(r.back) >= 1])
print(Mod(main.roots))
[1] 1.0005128 1.0005128 1.0002881 1.0002881 1.0000664 0.9999993

Соответствующие периоды

print(2*pi/Arg(main.roots))
[1]  10.01067 -10.01067  19.99437 -19.99437       Inf       Inf
plot(main.roots, xlim = c(-1,1), ylim = c(-1,1), asp = 1, xlab = "Re", ylab = "Im")
draw.circle(0,0,1,border = 'red')

Нашлось только 6 сигнальных корней, а должно быть 8.

Попробуем другой способ:

Составим систему и найдем коэффициенты по МНК

complex.LS <- function(series, series.roots){
   X <- mapply(FUN = function(root){root^(1:length(series))}, series.roots)
   Y <- cbind(series)
   list(coef = solve(Conj(t(X))%*%X) %*% Conj(t(X)) %*% Y,
        X = X)
}
coeffs.estimates <- complex.LS(long.reconstructed$trend + long.reconstructed$periodic, r)

Воспользуемся тем, что перед сигнальными корнями должны стоять максимальные коэффициенты

idx.roots <-order(Mod(coeffs.estimates$coef), decreasing = TRUE)[1:8]
main.roots<-r[idx.roots]
res.coeffs <- coeffs.estimates$coef[idx.roots]

Модули главных корней

print(Mod(main.roots))
[1] 0.9999324 1.0000664 0.6414650 1.0002881 1.0002881 0.9865705 0.9865705 0.9874613

Соответствующие периоды

print(2*pi/Arg(main.roots))
[1]       Inf       Inf       Inf -19.99437  19.99437  13.87755 -13.87755  13.58822
plot(main.roots, xlim = c(-1,1), ylim = c(-1,1), asp = 1, xlab = "Re", ylab = "Im")
draw.circle(0,0,1,border = 'red')

relation <- function(n){
  rbind(res.coeffs) %*% t(mapply(FUN = function(root){root^n}, main.roots))
}
plot(long.reconstructed$trend + long.reconstructed$periodic, type = 'l', ylab = "series")
lines(x = 1:1000, y = Re(relation(1:1000)), col = 'red')

Short series

plot(short, type='l')

spec.pgram(short, detrend = TRUE, fast = FALSE, log='no', taper=0, xaxt = 'n')
l <- seq(0, 0.5, 0.05)
axis(1, at = l, labels = l)

Ожидаем, что ранг ряда как минимум 4 (тренд + синус).

Decomposition

short.ssa <- ssa(short, kind='1d-ssa')

Собственные числа

plot(short.ssa)

Собственные вектора:

plot(short.ssa, type='vectors', idx = 1:20)

plot(short.ssa, type='paired', idx= c(1:20))

parestimate(short.ssa, groups = list(3:4, 5:6, 7:8, 11:12, 13:14))
$F1
   period     rate   |    Mod     Arg  |     Re        Im
   17.587   0.000000 |  1.00000   0.36 |  0.93686   0.34971

$F2
   period     rate   |    Mod     Arg  |     Re        Im
    5.151   0.000000 |  1.00000   1.22 |  0.34386   0.93902

$F3
   period     rate   |    Mod     Arg  |     Re        Im
    6.265   0.000000 |  1.00000   1.00 |  0.53780   0.84307

$F4
   period     rate   |    Mod     Arg  |     Re        Im
    3.133   0.000000 |  1.00000   2.01 | -0.42103   0.90705

$F5
   period     rate   |    Mod     Arg  |     Re        Im
    2.363  -0.000000 |  1.00000   2.66 | -0.88605   0.46360
plot(wcor(short.ssa, groups = as.list(1:20)))

short.reconstructed <- reconstruct(short.ssa, list(
  trend = c(1, 2),
  periodic = c(3:8,11:14)))
res.short <- residuals(short.reconstructed)
par(mfrow=c(4,1))
plot(short, type = 'l')
plot(short.reconstructed$trend, type='l', ylab = "trend")
plot(short.reconstructed$periodic, type='l', ylab = "periodic")
plot(res.short, type='l')

acf(res.short, lag.max = 50)

Projection SSA

Двойное центрирование

\(\mathcal{A}(\mathbf{X}) = [\mathcal{E}_1(\mathbf{X}) : \ldots : \mathcal{E}_1(\mathbf{X})]\), где \(\mathcal{E}_1(\mathbf{X})\) – вектор из средних по строкам

\(\mathcal{B}(\mathbf{X}) = [\mathcal{E}_{12}(\mathbf{X}) : \ldots : \mathcal{E}_{12}(\mathbf{X})]\), где \(\mathcal{E}_{12}(\mathbf{X})\) – вектор, в котором j-ая компонента равна среднему компонент \(X_j^{(c)} = X_i - \mathcal{E}_1(\mathbf{X})\).

При двойном центрировании применяем SVD к матрице \(\mathbf{A} = \mathcal{A}(\mathbf{X}) + \mathcal{B}(\mathbf{X})\)

Получим разложение \(\mathbf{X} = \mathbf{A} + \sum\limits_{i = 1}^d\sqrt{\lambda_i}U_iV_i^\mathrm{T}\), где \((\lambda_i, U_i, V_i)\) соответствуют \(S^* = (\mathbf{X} - \mathbf{A})(\mathbf{X} - \mathbf{A})^\mathrm{T}\).

Так как исходный тренд линейный, двойное центрирование поможет отделить линейную часть от колебаний (линейная часть попадет в \(\mathbf{A}\)).

short.projection.ssa <- ssa(x = short, column.projector = 'centering', row.projector = 'centering')
plot(short.projection.ssa)

plot(short.projection.ssa, type='vectors', idx = 1:20)

По виду собственных векторов видно, что тренд отделился лучше.

plot(short.projection.ssa, type='paired', idx= c(1:20))

parestimate(short.projection.ssa, groups = list(3:4, 5:6, 7:8, 11:12, 13:14), method ="esprit-ls")
$F1
   period     rate   |    Mod     Arg  |     Re        Im
   19.760  -0.003204 |  0.99680   0.32 |  0.94683   0.31165
  -19.760  -0.003204 |  0.99680  -0.32 |  0.94683  -0.31165

$F2
   period     rate   |    Mod     Arg  |     Re        Im
    5.332  -0.021225 |  0.97900   1.18 |  0.37444   0.90456
   -5.332  -0.021225 |  0.97900  -1.18 |  0.37444  -0.90456

$F3
   period     rate   |    Mod     Arg  |     Re        Im
    6.238  -0.037821 |  0.96289   1.01 |  0.51434   0.81400
   -6.238  -0.037821 |  0.96289  -1.01 |  0.51434  -0.81400

$F4
   period     rate   |    Mod     Arg  |     Re        Im
    3.194  -0.016631 |  0.98351   1.97 | -0.37971   0.90725
   -3.194  -0.016631 |  0.98351  -1.97 | -0.37971  -0.90725

$F5
   period     rate   |    Mod     Arg  |     Re        Im
    2.359  -0.067364 |  0.93485   2.66 | -0.83013   0.42993
   -2.359  -0.067364 |  0.93485  -2.66 | -0.83013  -0.42993
short.reconstructed <- reconstruct(short.projection.ssa, list(
  trend = c(1, 2),
  periodic = c(3:8,11:14)))
res.projection <- residuals(short.reconstructed)
par(mfrow=c(4,1))
plot(short, type = 'l')
plot(short.reconstructed$trend, type='l', ylab = "trend")
plot(short.reconstructed$periodic, type='l', ylab = "periodic")
plot(res.projection, type='l')

acf(res.projection, lag.max = 50)

Стало немного лучше, но все-таки что-то осталось.

Toeplitz SSA vs Basic SSA

Для коротких рядов, которые предполагаются стационарными рекомендуется заменять матрицу \(\mathbf{S} = \mathbf{X}\mathbf{X}^\mathrm{T}\) на автоковариационную матрицу \(\widetilde{\mathbf{C}}\) на этапе разложения

\(\widetilde{c}_{ij} = \frac{1}{N - |i - j|} \sum\limits_{m = 1}^{N - |i - j|} x_m x_{m + |i - j|}\), \(1 \leq i, j \leq L\).

Замечание:

Полученное раложение \(\mathbb{X} = \sum\limits_{i = 1}^L\sigma_iP_iQ_i^\mathrm{T}\), где \(P_i\) – с.в. \(\widetilde{\mathbf{C}}\), образуют ортонормированный базис в \(\mathbb{R}\), \(Q_i = \frac{\mathbb{X}^\mathrm{T}P_i}{\|\mathbb{X}^\mathrm{T}P_i\|}\). Это разложение не является сингулярным разложением, \(\sigma_i = \|\mathbb{X}^\mathrm{T}P_i\|\) вообще говоря не собственные числа \(\widetilde{\mathbf{C}}\).

Пример: ряд \(x_n = e^{\alpha n} cos(2\pi n/7) + \epsilon_n\)

set.seed(100)
eps <- rnorm(100)
alpha <- seq(0.0001, 0.01, 0.0001)
x <- mapply(FUN = function(a) {exp(a * (1:100)) * cos(2*pi*(1:100)/7) + eps}, alpha)
basic <- apply(x, 2, FUN = function(series){
  s <- ssa(series)
  rec <- reconstruct(s, groups = list(trend = c(1, 2)))
  per <- series - eps
  mse <- sum((rec$trend - per)^2)/length(per)
})
teoplitz <- apply(x, 2, FUN = function(series){
  s <- ssa(series, kind = "toeplitz-ssa")
  rec <- reconstruct(s, groups = list(trend = c(1, 2)))
  per <- series - eps
  mse <- sum((rec$trend - per)^2)/length(per)
})
plot(y = teoplitz, x = alpha, type = "l", ylab = "MSE", xlab = 'alpha', main = "Teoplitz vs Basic SSA")
lines(y = basic,  x = alpha,  col = 'red')
legend("topleft", c("Teoplitz", "Basic"), lty=c(1,1), lwd=c(2.5,2.5),col=c("black","red"))

Последовательный анализ реального ряда

uk <- read.csv("UK.csv", sep = ',', stringsAsFactors = FALSE)
uk <- ts(uk$EXPEND, start = c(1980, 1), frequency = 12)
df.uk <- data.frame(seq.Date(from = as.Date("1980-01-01"), to = as.Date("2016-12-01"), by = "month"), uk)
colnames(df.uk) <- c("DATE", "EXP")
head(uk, 24)
     Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
1980 117 116 166 180 202 290 298 441 388 260 175 105
1981 137 142 176 231 240 316 363 537 487 324 185 133
plot(uk)

Периодограмма ряда

spec.pgram(uk, detrend = TRUE, fast = FALSE, log='no', xaxt = 'n')
axis(1, at = c(0,1,2,3,4,5,6), labels = c('0', '1/12', '2/12', '3/12', '4/12', '5/12', '6/12'))

Сглаживание

  • Тренд сложной формы, возможно, не описывается рядом конечной размерности
  • Если выберем большое окно, компоненты тренда смешаются с периодическими
  • Возьмем маленькое окно: хорошо выделим тренд, но смешаются остальные компоненты
  • Решение: сначала выделим тренд с маленьким окном, потом применим SSA с большим окном к остатку.

Выбираем небольшую длину окна кратную периоду сезонной компоненты, L = 24.

uk.ssa <- ssa(uk, L = 24)

Вклады

plot(uk.ssa)

Видно, что собственные числа периодических компонент близкие. Скорее всего эти компоненты смешаются (проблема с сильной разделимостью).

Собственные вектора

plot(uk.ssa, type="vectors", idx=1:12)

plot(uk.ssa, type="paired", idx=1:11)

Восстановленные по элементарным компонентам

plot(uk.ssa, type ='series', groups = as.list(1:12))

Первая элементарная компонента соответствует тренду, дальше идут периодики.

Выделение тренда

uk.trend <- reconstruct(uk.ssa, groups = list(1))
plot(uk)
lines(uk.trend$F1, col = 'red')

uk.detrended <- residuals(uk.trend)

Периодограмма остатка

spec.pgram(uk.detrended, detrend = FALSE, fast = FALSE, log='no', xaxt = 'n')
axis(1, at = c(0,1,2,3,4,5,6), labels = c('0', '1/12', '2/12', '3/12', '4/12', '5/12', '6/12'))

Ожидаем, что дальше будет достаточно выделить 9 компонент (синус с периодом 2 имеет ранг 1).

Сезонность

Возьмем максимальное \(L \leq N/2\) кратное 12, т.е. 216 (длина ряда 444).

uk.detrended.ssa <- ssa(uk.detrended, L=216)
plot(uk.detrended.ssa)

Видно ступеньки, которые, возможно, соответствуют синусоидам.

plot(uk.detrended.ssa, type = "paired", idx = 1:30, plot.contrib = FALSE)

Заметны компоненты, соответствующие периодам 12, 6, 4 и 2.4.

Компонента, соответствующая периоду 2.

plot(uk.detrended.ssa, type = "vectors", idx = 9, plot.contrib = FALSE)

Проверим

parestimate(uk.detrended.ssa, groups = list(1:9), method = "esprit-ls")
   period     rate   |    Mod     Arg  |     Re        Im
    3.995   0.005162 |  1.00518   1.57 | -0.00196   1.00517
   -3.995   0.005162 |  1.00518  -1.57 | -0.00196  -1.00517
   11.991   0.004041 |  1.00405   0.52 |  0.86934   0.50236
  -11.991   0.004041 |  1.00405  -0.52 |  0.86934  -0.50236
    2.000   0.003889 |  1.00390   3.14 | -1.00390   0.00000
    5.995   0.003856 |  1.00386   1.05 |  0.50114   0.86983
   -5.995   0.003856 |  1.00386  -1.05 |  0.50114  -0.86983
    2.401   0.003558 |  1.00356   2.62 | -0.86881   0.50231
   -2.401   0.003558 |  1.00356  -2.62 | -0.86881  -0.50231

W-корреляционная матрица

plot(wcor(uk.detrended.ssa, groups = as.list(1:20)))

uk.seasonality <- reconstruct(uk.detrended.ssa, groups = list(1:9))
res1 <- residuals(uk.seasonality)
par(mfrow=c(4,1))
plot(uk, type = 'l', ylab='Original')
plot(uk.trend$F1, type='l', ylab='Trend')
plot(uk.seasonality$F1, type='l', ylab = 'Seasonal Component')
plot(res1, type='l', ylab='Residuals')

В остатке остался тренд.

acf(res1)

spec.pgram(res1, detrend = FALSE, fast = FALSE, log='no', xaxt = 'n')
axis(1, at = c(0,1,2,3,4,5,6), labels = c('0', '1/12', '2/12', '3/12', '4/12', '5/12', '6/12'))

Оценка дисперсии шума

sigma.n.sqr <- ssa(res1^2, L = 30)
sigma.n <- sqrt(reconstruct(sigma.n.sqr, groups = list(1))$F1)
plot(res1, type='l')
lines(sigma.n, type = 'l', col='blue')
lines(-sigma.n, type='l', col='blue')

Forecasting

Рассмотрим тот же самый ряд, но без последних шести лет.

uk.train <- head(uk, -72)
plot(uk.train)

uk.train.ssa <- ssa(uk.train, L = 24)
uk.train.trend <- reconstruct(uk.train.ssa, groups = 1)
uk.train.detrended <- residuals(uk.train.trend)
uk.train.detrended.ssa <- ssa(uk.train.detrended, L = 180)
uk.train.seasonality <- reconstruct(uk.train.detrended.ssa, groups = list(1:9))

R-forecasting

Продолжаем ряд с помощью min-norm LRR.

uk.trend.forecast <- predict(uk.train.ssa, method = "recurrent", len = 72, groups = 1)
uk.seasonality.forecast <- predict(uk.train.detrended.ssa, method = "recurrent", len = 72, groups = list(1:9))
plot(uk, xlim = c(2000, 2020))
lines(uk.trend.forecast + uk.seasonality.forecast, col = 'red')

MSE

sum((tail(uk, 72) - (uk.trend.forecast + uk.seasonality.forecast))^2)/72
[1] 117129.6

V-forecasting

uk.trend.forecast <- predict(uk.train.ssa, method = "vector", len = 72, groups = 1)
uk.seasonality.forecast <- predict(uk.train.detrended.ssa, method = "vector", len = 72, groups = list(1:9))
plot(uk, xlim = c(2000, 2020))
lines(uk.trend.forecast + uk.seasonality.forecast, col = 'red')

MSE

sum((tail(uk, 72) - (uk.trend.forecast + uk.seasonality.forecast))^2)/72
[1] 76774.87

Доверительные интервалы

Bootstrap:

  • Выделяем сигнал с помощью SSA
  • Оцениваем дисперсию остатка и много раз моделируем гауссовский шум
  • Выделяем сигнал из оцененного сигнала + моделированного шума
  • Получим много оценок сигнала
  • Строим продолжения
  • Отбрасываем \((1 - \gamma)/2\) с концов
uk.trend.forecast <- predict(uk.train.ssa, method = "bootstrap-recurrent", len = 72, groups = 1)
uk.seasonality.forecast <- predict(uk.train.detrended.ssa, method = "bootstrap-recurrent", len = 72, groups = list(1:9))
plot(uk, xlim = c(2010, 2018))
lines(uk.trend.forecast[,1] + uk.seasonality.forecast[,1], col = 'red')
lines(uk.trend.forecast[,2] + uk.seasonality.forecast[,2], col = 'blue',lty="dashed")
lines(uk.trend.forecast[,3] + uk.seasonality.forecast[,3], col = 'blue',lty="dashed")

Заполнение пропусков

Рассмотрим ряд

uk.head <- head(uk, - 120)
plot(uk.head)

Добавим пропуски в ряд

uk.na <- uk.head
missed <- uk.head[121:180]
uk.na[121:180] = NA
plot(uk.na, type = 'l')

Алгоритм

Subspase-based method:

  • На этапе декомпозиции, исключаем вектора вложения, содержащие пропущенные элементы
  • Из оставшихся векторов составляем траекторную матрицу
  • SVD
  • Проекция на \(\mathcal{L}_r\)
  • Проекция неполных векторов на \(\mathcal{L}_r\).
  • “предсказание во внутрь” с помощью лрф, построенной по имеющимся данным. Предсказываем слева и справа с линейно убывающими весами, а потом усредняем
  • Диагональное усреднение

Итеративный метод:

  • Заполняем пропуски, например, средним
  • r и L фиксированы (например выбрали с помощью cv)
  • Применим SSA, вставим интересующую нас часть в исходный ряд, снова применим SSA и так далее

Минус: для прогноза не годится.

uk.na.ssa <- ssa(uk.na, L = 72)

Предсказание во внутрь

g <- gapfill(uk.na.ssa, groups = list(1:8))
plot(unclass(uk.head), type = 'l')
lines(y = unclass(g[121:180]), x = 121:180, col = 'red', lty = 2)

MSE

sum((g[121:180] - missed)^2)/60
[1] 10470.2

Итеративный метод

g.it <- igapfill(uk.na.ssa, groups = list(1:8))
plot(unclass(uk.head), type = 'l')
lines(y = unclass(g.it[121:180]), x = 121:180, col = 'red', lty = 2)

MSE

sum((g.it[121:180] - missed)^2)/60
[1] 8747.204

Итеративный метод оказался точнее.

2D-SSA для разложения изображения

Рассмотрим фотографию спутника Сатурна, Дионы (Источник).

Прочитали картинку

dione <- readJPEG("Dione.jpg")
image(dione, col = grey(seq(0, 1, length = 256)))

Добавим шум

set.seed(100)
noise.matrix <- apply(dione, 2, function(x){rnorm(length(x), sd = 0.3)})
dione.noised <- dione + noise.matrix
image(dione.noised, col = grey(seq(0, 1, length = 256)))

2d-ssa

dione.ssa <- ssa(dione.noised, kind = "2d-ssa", L = c(25,25))

Собственные вектора

plot(dione.ssa, type = "vectors", idx = 1:30, cuts = 255, layout = c(10, 2), plot.contrib = FALSE)

w-корреляционная матрица

plot(wcor(dione.ssa, groups = 1:30),scales = list(at = seq(10, 30, 5)))

Сгруппируем компоненты с частотами похожего вида

dione.reconstructed <- reconstruct(dione.ssa, groups = list(I1 = c(1:13), I2 = c(14:15), I3 = c(16:17)))

Накопленные суммы компонент

plot(dione.reconstructed, cuts = 255, layout = c(5, 1), par.strip.text = list(cex = 0.75), type = "cumsum", at = "free")

Shaped SSA

Уберем черный фон

dione.without.black <- apply(dione, 2, FUN = function(col){
  sapply(col, FUN = function(element){
    if(element <= 0.1){
      return(NA)
    } else{
      return(element)
    }
  })
})
image(dione.without.black, col = grey(seq(0, 1, length = 256)))

Добавим шум

dione.without.black.noised <- dione.without.black + noise.matrix
image(dione.without.black.noised, col = grey(seq(0, 1, length = 256)))

Попробуем изменить форму окна на круг.

dione.ssa.shaped <- ssa(dione.without.black.noised, kind = "2d-ssa", wmask = circle(20))
Some field elements were not covered by shaped window. 1253 elements will be ommited
plot(dione.ssa.shaped, type = "vectors", idx = 1:30, cuts = 255, layout = c(10, 2), plot.contrib = FALSE)

w-корреляционная матрица

plot(wcor(dione.ssa.shaped, groups = 1:30),scales = list(at = seq(10, 30, 5)))

По матрице можно сделать вывод о том, что приблизительно 10 первых компонент относятся к сигналу, а остальное это шум.

Сгруппируем компоненты с частотами похожего вида

dione.reconstructed.shaped <- reconstruct(dione.ssa.shaped, groups = list(I1 = c(1:6), I2 = c(7:12), I3 = c(13:19)))

Накопленные суммы компонент

plot(dione.reconstructed.shaped, cuts = 255, layout = c(5, 1), par.strip.text = list(cex = 0.75), type = "cumsum", at = "free")

На вид, исходная картинка лучше отделилась от шума в случае shaped SSA.

Exponential Smoothing

(ES vs SSA vs ARIMA)

es.fit <- es(uk.train, h = 72)
Forming the pool of models based on... ANN, ANA, ANM, AAM, Estimation progress:    45%55%64%73%82%91%100%... Done! 

MSE

sum((tail(uk,72) - es.fit$forecast)^2)/72
[1] 91021.91

Модель

es.fit$formula
[1] "y[t] = (l[t-1] + b[t-1]) * s[t-12] * e[t]"
uk.trend.forecast <- predict(uk.train.ssa, method = "recurrent", len = 72, groups = 1)
uk.seasonality.forecast <- predict(uk.train.detrended.ssa, method = "recurrent", len = 72, groups = list(1:9))
plot(uk, xlim = c(2010, 2018))
lines(uk.trend.forecast + uk.seasonality.forecast, col = 'red')

sum((tail(uk,72) - (uk.trend.forecast + uk.seasonality.forecast))^2)/72
[1] 117129.6
auto.fit.uk <- auto.arima(uk.train, trace = TRUE, stepwise = FALSE, allowdrift = FALSE)

 ARIMA(0,0,0)(0,1,0)[12]                    : 4609.677
 ARIMA(0,0,0)(0,1,1)[12]                    : 4605.92
 ARIMA(0,0,0)(0,1,2)[12]                    : 4570.102
 ARIMA(0,0,0)(1,1,0)[12]                    : 4613.276
 ARIMA(0,0,0)(1,1,1)[12]                    : Inf
 ARIMA(0,0,0)(1,1,2)[12]                    : Inf
 ARIMA(0,0,0)(2,1,0)[12]                    : 4575.118
 ARIMA(0,0,0)(2,1,1)[12]                    : Inf
 ARIMA(0,0,0)(2,1,2)[12]                    : Inf
 ARIMA(0,0,1)(0,1,0)[12]                    : 4507.381
 ARIMA(0,0,1)(0,1,1)[12]                    : 4508.452
 ARIMA(0,0,1)(0,1,2)[12]                    : 4485.141
 ARIMA(0,0,1)(1,1,0)[12]                    : 4519.165
 ARIMA(0,0,1)(1,1,1)[12]                    : 4513.546
 ARIMA(0,0,1)(1,1,2)[12]                    : Inf
 ARIMA(0,0,1)(2,1,0)[12]                    : 4500.653
 ARIMA(0,0,1)(2,1,1)[12]                    : Inf
 ARIMA(0,0,1)(2,1,2)[12]                    : Inf
 ARIMA(0,0,2)(0,1,0)[12]                    : 4437.354
 ARIMA(0,0,2)(0,1,1)[12]                    : 4430.973
 ARIMA(0,0,2)(0,1,2)[12]                    : 4420.724
 ARIMA(0,0,2)(1,1,0)[12]                    : 4438.837
 ARIMA(0,0,2)(1,1,1)[12]                    : 4436.083
 ARIMA(0,0,2)(1,1,2)[12]                    : Inf
 ARIMA(0,0,2)(2,1,0)[12]                    : 4442.367
 ARIMA(0,0,2)(2,1,1)[12]                    : Inf
 ARIMA(0,0,3)(0,1,0)[12]                    : 4420.287
 ARIMA(0,0,3)(0,1,1)[12]                    : 4405.62
 ARIMA(0,0,3)(0,1,2)[12]                    : 4397.304
 ARIMA(0,0,3)(1,1,0)[12]                    : 4411.34
 ARIMA(0,0,3)(1,1,1)[12]                    : 4411.748
 ARIMA(0,0,3)(2,1,0)[12]                    : 4422.04
 ARIMA(0,0,4)(0,1,0)[12]                    : 4401.569
 ARIMA(0,0,4)(0,1,1)[12]                    : 4382.084
 ARIMA(0,0,4)(1,1,0)[12]                    : 4386.684
 ARIMA(0,0,5)(0,1,0)[12]                    : 4383.458
 ARIMA(1,0,0)(0,1,0)[12]                    : 4407.718
 ARIMA(1,0,0)(0,1,1)[12]                    : 4344.179
 ARIMA(1,0,0)(0,1,2)[12]                    : 4340.676
 ARIMA(1,0,0)(1,1,0)[12]                    : 4357.705
 ARIMA(1,0,0)(1,1,1)[12]                    : 4352.702
 ARIMA(1,0,0)(1,1,2)[12]                    : Inf
 ARIMA(1,0,0)(2,1,0)[12]                    : 4366.902
 ARIMA(1,0,0)(2,1,1)[12]                    : 4366.76
 ARIMA(1,0,0)(2,1,2)[12]                    : 4368.57
 ARIMA(1,0,1)(0,1,0)[12]                    : 4345.68
 ARIMA(1,0,1)(0,1,1)[12]                    : 4280.329
 ARIMA(1,0,1)(0,1,2)[12]                    : 4277.132
 ARIMA(1,0,1)(1,1,0)[12]                    : 4290.246
 ARIMA(1,0,1)(1,1,1)[12]                    : 4288.556
 ARIMA(1,0,1)(1,1,2)[12]                    : Inf
 ARIMA(1,0,1)(2,1,0)[12]                    : 4301.595
 ARIMA(1,0,1)(2,1,1)[12]                    : 4302.477
 ARIMA(1,0,2)(0,1,0)[12]                    : 4346.712
 ARIMA(1,0,2)(0,1,1)[12]                    : 4281.757
 ARIMA(1,0,2)(0,1,2)[12]                    : 4278.092
 ARIMA(1,0,2)(1,1,0)[12]                    : 4291.57
 ARIMA(1,0,2)(1,1,1)[12]                    : 4289.544
 ARIMA(1,0,2)(2,1,0)[12]                    : 4302.708
 ARIMA(1,0,3)(0,1,0)[12]                    : 4348.464
 ARIMA(1,0,3)(0,1,1)[12]                    : 4277.812
 ARIMA(1,0,3)(1,1,0)[12]                    : 4291.172
 ARIMA(1,0,4)(0,1,0)[12]                    : 4346.347
 ARIMA(2,0,0)(0,1,0)[12]                    : 4356.483
 ARIMA(2,0,0)(0,1,1)[12]                    : 4294.508
 ARIMA(2,0,0)(0,1,2)[12]                    : 4293.482
 ARIMA(2,0,0)(1,1,0)[12]                    : 4309.562
 ARIMA(2,0,0)(1,1,1)[12]                    : 4304.906
 ARIMA(2,0,0)(1,1,2)[12]                    : Inf
 ARIMA(2,0,0)(2,1,0)[12]                    : 4318.985
 ARIMA(2,0,0)(2,1,1)[12]                    : 4318.889
 ARIMA(2,0,1)(0,1,0)[12]                    : 4347.574
 ARIMA(2,0,1)(0,1,1)[12]                    : 4282.203
 ARIMA(2,0,1)(0,1,2)[12]                    : 4278.389
 ARIMA(2,0,1)(1,1,0)[12]                    : 4292.378
 ARIMA(2,0,1)(1,1,1)[12]                    : 4289.94
 ARIMA(2,0,1)(2,1,0)[12]                    : 4303.347
 ARIMA(2,0,2)(0,1,0)[12]                    : 4347.527
 ARIMA(2,0,2)(0,1,1)[12]                    : 4284.437
 ARIMA(2,0,2)(1,1,0)[12]                    : 4294.897
 ARIMA(2,0,3)(0,1,0)[12]                    : 4349.054
 ARIMA(3,0,0)(0,1,0)[12]                    : 4354.896
 ARIMA(3,0,0)(0,1,1)[12]                    : 4293.657
 ARIMA(3,0,0)(0,1,2)[12]                    : 4291.62
 ARIMA(3,0,0)(1,1,0)[12]                    : 4306.162
 ARIMA(3,0,0)(1,1,1)[12]                    : 4303.185
 ARIMA(3,0,0)(2,1,0)[12]                    : 4316.591
 ARIMA(3,0,1)(0,1,0)[12]                    : 4350.227
 ARIMA(3,0,1)(0,1,1)[12]                    : 4280.178
 ARIMA(3,0,1)(1,1,0)[12]                    : 4293.699
 ARIMA(3,0,2)(0,1,0)[12]                    : 4350.246
 ARIMA(4,0,0)(0,1,0)[12]                    : 4347.801
 ARIMA(4,0,0)(0,1,1)[12]                    : 4286.691
 ARIMA(4,0,0)(1,1,0)[12]                    : 4298.339
 ARIMA(4,0,1)(0,1,0)[12]                    : 4349.489
 ARIMA(5,0,0)(0,1,0)[12]                    : 4350.566
arima.prediction.uk <- predict(auto.fit.uk, 72)
plot(uk, xlim = c(2010, 2018))
lines(arima.prediction.uk$pred, col = 'red')
lines(arima.prediction.uk$pred + arima.prediction.uk$se, lty = 'dashed', col = 'blue')
lines(arima.prediction.uk$pred - arima.prediction.uk$se, lty = 'dashed', col = 'blue')

MSE

sum((tail(uk,72) - arima.prediction.uk$pred)^2)/72
[1] 477969.6
LS0tCnRpdGxlOiAiU1NBIgphdXRob3I6ICLQktC70LDQtNC40LzQuNGAINCQ0LPQtdC10LIiCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgdG9jOiB5ZXMKICAgIHRvY19kZXB0aDogMwogICAgdG9jX2Zsb2F0OiB5ZXMKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKICAgIHRvY19kZXB0aDogJzMnCiAgICB0b2NfZmxvYXQ6IHllcwotLS0KCmBgYHtyLCBlY2hvPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQpsaWJyYXJ5KFJzc2EpCmxpYnJhcnkocGxvdHJpeCkKbGlicmFyeShqcGVnKQpsaWJyYXJ5KHNtb290aCkKYGBgCgoKYGBge3J9CnRhYmxlIDwtIHJlYWQuY3N2Migic3NhLmNzdiIpCmxvbmcgPC0gdGFibGUkQ09MMQpzaG9ydCA8LSBuYS5vbWl0KHRhYmxlJENPTDcpCmBgYAoKI0xvbmcgc2VyaWVzCmBgYHtyfQpwbG90KGxvbmcsIHR5cGU9J2wnKQpgYGAKCmBgYHtyfQpzcGVjLnBncmFtKGxvbmcsIGRldHJlbmQgPSBUUlVFLCBmYXN0ID0gRkFMU0UsIGxvZz0nbm8nLCB0YXBlcj0wKQpgYGAK0J/RgNC10LTQv9C+0LvQsNCz0LXQvCwg0YfRgtC+INC00L7RgdGC0LDRgtC+0YfQvdC+INGB0LPRgNGD0L/Qv9C40YDQvtCy0LDRgtGMIDgg0LrQvtC80L/QvtC90LXQvdGCLCDRgi7Qui4g0LLQvtC30LzQvtC20L3Qviwg0YDQsNC90LMg0YDQsNCy0LXQvSA4ICjQu9C40L3QtdC50L3Ri9C5INGC0YDQtdC90LQgKyAzINC60L7RgdC40L3Rg9GB0LApLgoKCgojI9Ca0LDQuiDQstGL0LHQuNGA0LDRgtGMIEwKCtCi0LDQuiDQutCw0Log0YLQvtGH0L3QvtC5INGA0LDQt9C00LXQu9C40LzQvtGB0YLQuCDQsiDRgNC10LDQu9GM0L3Ri9GFINGA0Y/QtNCw0YUg0L3QtSDQsdGL0LLQsNC10YIsINC90LDRgSDQuNC90YLQtdGA0LXRgdGD0LXRgiDQsNGB0LjQvNGC0L7RgtC40YfQtdGB0LrQsNGPINGA0LDQt9C00LXQu9C40LzQvtGB0YLRjC4KCtCSINCx0L7Qu9GM0YjQuNC90YHRgtCy0LUg0YHQu9GD0YfQsNC10LIg0YHQutC+0YDQvtGB0YLRjCDQsNGB0LjQvNC/0YLQvtGC0LjRh9C10YHQutC+0Lkg0YDQsNC30LTQtdC70LjQvNC40L7RgdGC0Lgg0L/RgNC+0L/QvtGA0YbQuNC+0L3QsNC70YzQvdCwICQxL1xtaW4oTCwgSykkLCAkSyA9IE4gLSBMICsgMSQsINC/0L7RjdGC0L7QvNGDINCy0YvQsdC40YDQsNC10LwgJEwkINCx0LvQuNC30LrQvtC1INC6ICROLzIkLgoK0JXRgdC70Lgg0LzRiyDRhdC+0YLQuNC8INC+0YLQtNC10LvRj9GC0Ywg0LrQvtGB0LjQvdGD0YEg0YEg0L3QtdC60L7RgtC+0YDRi9C8INC/0LXRgNC40L7QtNC+0LwsINGC0L4g0LTQu9GPINGD0LvRg9GH0YjQtdC90LjRjyDRgNCw0LfQtNC10LvQuNC80L7RgdGC0Lgg0YDQtdC60L7QvNC10L3QtNGD0LXRgtGB0Y8g0LHRgNCw0YLRjCDQvtC60L3QviDQutGA0LDRgtC90L7QtSDQv9C10YDQuNC+0LTRgyAo0YHQu9C10LTRg9C10YIg0LjQtyDRg9GB0LvQvtCy0LjQuSDRgtC+0YfQvdC+0Lkg0YHQu9Cw0LHQvtC5INGA0LDQt9C00LXQu9C40LzQvtGB0YLQuCkuCgojI9Ch0LvQsNCx0LDRjyDQuCDRgdC40LvRjNC90LDRjyDRgNCw0LfQtNC10LvQuNC80L7RgdGC0YwKCiog0KHQu9Cw0LHQsNGPINGA0LDQt9C00LXQu9C40LzQvtGB0YLRjCAtLSDQvtGA0YLQvtCz0L7QvdCw0LvRjNC90L7RgdGC0Ywg0YLRgNCw0LXQutGC0L7RgNC90YvRhSDQv9C+0LTQv9GA0L7RgdGC0YDQsNC90YHRgtCyCiog0KHQuNC70YzQvdCw0Y8g0LDQt9C00LXQu9C40LzQvtGB0YLRjCAtLSDRgdC70LDQsdCw0Y8g0YDQsNC30LTQtdC70LjQvNC+0YHRgtGMICsg0LIg0YDQsNC30L3Ri9GFINC60L7QvNC/0L7QvdC10L3RgtCw0YUgKFNWRCkg0YDQsNC30L3Ri9C1INGB0L7QsdGB0YLQstC10L3QvdGL0LUg0YfQuNGB0LvQsAoKRGVjb21wb3NpdGlvbgpgYGB7cn0Kc3NhLmxvbmcgPC0gc3NhKGxvbmcsIGtpbmQ9JzFkLXNzYScpCmBgYArQodC+0LHRgdGC0LLQtdC90L3Ri9C1INGH0LjRgdC70LA6CgokXGxhbWJkYV9pID0gXHxcbWF0aGJme1h9X2lcfF4yX3tcbWF0aGNhbHtGfX0kLCAkXGxhbWJkYV9pLyBcfFxtYXRoYmZ7WH1cfF4yX3tcbWF0aGNhbHtGfX0kIC0tINCy0LrQu9Cw0LQg0LrQvtC80L/QvtC90LXQvdGC0YsKYGBge3J9CnBsb3Qoc3NhLmxvbmcpCmBgYAoKCiMj0JrQsNC6INC40LTQtdC90YLQuNGE0LjRhtC40YDQvtCy0LDRgtGMINGC0YDQtdC90LQ/CgrQpdC+0YLQuNC8INCy0YvQtNC10LvQuNGC0Ywg0LLRgdC1INGN0LvQtdC80LXQvdGC0LDRgNC90YvQtSDQutC+0LzQv9C+0L3QtdC90YLRiywg0LrQvtGC0L7RgNGL0LUg0LjQvNC10Y7RgiDQvNC10LTQu9C10L3QvdC+INC80LXQvdGP0Y7RidC40LXRgdGPINGB0L7QsdGB0YLQstC10L3QvdGL0LUg0LLQtdC60YLQvtGA0LAuCgoK0KHQvtCx0YHRgtCy0LXQvdC90YvQtSDQstC10YLQvtGA0LAg0L7QsdGA0LDQt9GD0Y7RgiDQsdCw0LfQuNGBINGC0YDQsNC10LrRgtC+0YDQvdC+0LPQviDQv9C+0LTQv9GA0L7RgdGC0YDQsNC90YHRgtCy0LAsINGB0LvQtdC00L7QstCw0YLQtdC70YzQvdC+LCDQvtC90Lgg0LjQvNC10Y7RgiDQstC40LQg0YHQvtC+0YLQstC10YLRgdGC0LLRg9GO0YnQuNGFINC60L7QvNC/0L7QvdC10L3RgiDRgNGP0LTQsC4KCtCh0L7QsdGB0YLQstC10L3QvdGL0LUg0LLQtdC60YLQvtGA0LA6CmBgYHtyLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9MTB9CnBsb3Qoc3NhLmxvbmcsIHR5cGU9J3ZlY3RvcnMnLCBpZHggPSAxOjE1KQpgYGAK0J/QtdGA0LLRi9C1INC00LLQsCAtLSDQu9C40L3QtdC50L3Ri9C5INGC0YDQtdC90LQsINC00LDQu9GM0YjQtSDQuNC00YPRgiDQv9C10YDQuNC+0LTQuNGH0LXRgdC60LjQtSDQutC+0LzQv9C+0L3QtdC90YLRiy4KCiMj0JrQsNC6INC40LTQtdC90YLQuNGE0LjRhtC40YDQvtCy0LDRgtGMINC/0LXRgNC40L7QtNC40YfQvdC+0YHRgtGMCiAKINCf0L7RgdC80L7RgtGA0LjQvCDQvdCwINGB0LrQsNGC0YLQtdGA0L/Qu9C+0YLRiyDQv9Cw0YAg0YHQvtCx0YHRgtCy0LXQvdC90YvRhSDQstC10LrRgtC+0YDQvtCyLgogCiAqINCX0L3QsNC10LwsINGH0YLQviDRjdC60YHQv9C+0L3QtdC90YbQuNCw0LvRjNC90L4g0LzQvtC00YPQu9C40YDQvtCy0LDQvdC90YvQuSDQutC+0YHQuNC90YPRgSDRgdC+0L7RgtCy0LXRgtGB0YLQstGD0LXRgiDQtNCy0YPQvCDRgdC+0LHRgdGC0LLQtdC90L3Ri9C8INGC0YDQvtC50LrQsNC8INGBINC/0YDQuNC80LXRgNC90L4g0L7QtNC40L3QsNC60L7QstGL0LzQuCDRgdC+0LHRgdGC0LLQtdC90L3Ri9C80Lgg0YfQuNGB0LvQsNC80LggKCRMdyQgLS0g0YbQtdC70YvQtSkuINCe0YLRgdGO0LTQsCDRgdC70LXQtNGD0LXRgiwg0YfRgtC+INGN0YLQuCBTVkQg0LrQvtC80L/QvtC90LXQvdGC0Ysg0YHQutC+0YDQtdC1INCy0YHQtdCz0L4g0LjQtNGD0YIg0LTRgNGD0LMg0LfQsCDQtNGA0YPQs9C+0LwKICog0K3RgtC4INC00LLQtSDQv9C10YDQuNC+0LTQuNGH0LXRgdC60LjQtSDQutC+0LzQv9C+0L3QtdC90YLRiyAo0YHQvtCx0YHRgtCy0LXQvdC90YvQtSDQstC10LrRgtC+0YDQsCksINC+0YLQu9C40YfQsNGO0YLRgdGPINC90LAgJFxwaS8yJCDQv9C+INGE0LDQt9C1ICjQv9GA0Lgg0YbQtdC70YvRhSAkTHckKS4KIAog0KLQsNC60LjQvCDQvtCx0YDQsNC30L7QvCwg0LLQuNC00LjQvNGL0LUg0KIt0YPQs9C+0LvRjNC90LjQutC4INGB0L7QvtGC0LLQtdGC0YHRgtCy0YPRjtGCINC/0LXRgNC40L7QtNC40YfQtdGB0LrQuNC8INC60L7QvNC/0L7QvdC10L3RgtCw0Lwg0YEg0L/QtdGA0LjQvtC00L7QvCDQoi4KCmBgYHtyICwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTEwfQpwbG90KHNzYS5sb25nLCB0eXBlPSdwYWlyZWQnLCBpZHg9IGMoMToyMCkpCmBgYAoK0JfQsNC80LXRgtC90Ysg0LrQvtC80L/QvtC90LXQvdGC0Ysg0YEg0L/QtdGA0LjQvtC00LDQvNC4IDIwLCAxMCDQuCA2LiDQldGB0YLRjCDQutC+0LzQv9C+0L3QtdC90YLQsCwg0L/QvtGF0L7QttCw0Y8g0L3QsCDQv9C10YDQuNC+0LQgMi40LCDQutC+0LzQv9C+0L3QtdC90YLQsCDQv9C+0YXQvtC20LDRjyDQvdCwINC/0LXRgNC40L7QtCB+Ni4gCgojI9Ct0LvQtdC80LXQvdGC0LDRgNC90YvQtSDQstC+0YHRgdGC0LDQvdC+0LLQu9C10L3QvdGL0LUg0LrQvtC80L/QvtC90LXQvdGC0YsKCmBgYHtyfQpwbG90KHNzYS5sb25nLCB0eXBlPSdzZXJpZXMnLCBncm91cHMgPSBsaXN0KDE6MiwgMzo0LCA1OjYsIDc6OCwgOToxMCwgMTE6MTIpKQpgYGAKCiMj0J7RhtC10L3QutCwINC/0LDRgNCw0LzQtdGC0YDQvtCyIChFU1BSSVQpCgokeF9uID0gXHN1bVxsaW1pdHNfe2ogPSAxfV5yIENfaiBcbXVfal5uICsgXGVwc2lsb25fbiQgLS0g0YDRj9C0LCAkXG11X2ogPSBccmhvX2plXnsyXHBpIGkgXG9tZWdhX2p9JCAtLSDQutC+0YDQvdC4INGFLtC/LgoK0JDQu9Cz0L7RgNC40YLQvCBFU1BSSVQtTFM6CgoqINCf0YDQuNC80LXQvdC40LIgU1NBINC4INC/0L7Qu9GD0YfQuNCyIFNWRCDRgNCw0LfQu9C+0LbQtdC90LjQtSwg0YHQvtGB0YLQsNCy0LvRj9C10LwgciDRgdC+0LHRgdGC0LLQtdC90L3Ri9GFINCy0LXQutGC0L7RgNC+0LIg0LIg0LzQsNGC0YDQuNGG0YMgJFxtYXRoYmJ7VX0kCiogJFx3aWRlaGF0e1xtYXRoYmJ7RH19ID0gXHVuZGVybGluZXtcbWF0aGJie1V9fV4tXGJhcntcbWF0aGJie1V9fSQKKiDQodC+0LHRgdGC0LLQtdC90L3Ri9C1INGH0LjQu9GB0LAgXHdpZGVoYXR7XG1hdGhiYntEfX0gLS0g0L7RhtC10L3QutC4INGB0LjQs9C90LDQu9GM0L3Ri9GFINC60L7RgNC90LXQuQoKYGBge3J9CnBhcmVzdGltYXRlKHNzYS5sb25nLCBncm91cHMgPSBsaXN0KDE6MTIpLCBtZXRob2QgPSAiZXNwcml0LWxzIikKYGBgCgoKIyPQmtC+0LzQv9C+0L3QtdC90YLRiyDRgdC80LXRiNCw0LvQuNGB0YwuINCa0LDQuiDQv9C+0L3Rj9GC0YwsINGN0YLQviDRgdC70LDQsdCw0Y8g0LjQu9C4INGB0LjQu9GM0L3QsNGPINGA0LDQt9C00LXQu9C40LzQvtGB0YLRjD8KCtCd0LXRgiDRgdC70LDQsdC+0Lkg0YDQsNC30LTQtdC70LjQvNC+0YHRgtC4IC0tINC+0YLRgdGD0YLRgdGC0LLQuNC1INC+0YDRgtC+0LPQvtC90LDQu9GM0L3QvtGB0YLQuCDRgtGA0LDQtdC60YLQvtGA0L3Ri9GFINC/0L7QtNC/0YDQvtGB0YLRgNCw0L3RgdGC0LIuCgoqINCf0L7RgdC80L7RgtGA0LXRgtGMINC90LAg0LzQsNGC0YDQuNGG0YMg0LLQt9Cy0LXRiNC10L3QvdGL0YUg0LrQvtGA0YDQtdC70Y/RhtC40LkgJFxyaG9fe2lqfShYX05eeyhpKX0sWF9OXnsoail9KSA9IFxmcmFjeyhYXnsoaSl9X04sIFheeyhqKX1fTilfd317XHxYXnsoaSl9X05cfF93XHxYXnsoail9X05cfF93fSQuCgrQn9C+INGC0LDQutC+0Lkg0LzQsNGC0YDQuNGG0LUg0L/QvtC50LzQtdC8LCDRhdC+0YDQvtGI0L4g0LvQuCDQvNGLINGA0LDQt9C00LXQu9C40LvQuCDQutC+0LzQv9C+0L3QtdC90YLRiy4g0J/QvtC50LzQtdC8LCDQutCw0LrQuNC1INC60L7QvNC/0L7QvdC10L3RgtGLINC00LXQu9C40YLRjCDQvdC1INGB0YLQvtC40YIuCgoqINCc0L7QttC90L4g0L/QvtGB0LzQvtGC0YDQtdGC0Ywg0L3QsCDRh9Cw0YHRgtC+0YLQvdGL0LUg0LrQvtGN0YTRhNC40YbQuNC10L3RgtGLINC60L7RgNGA0LXQu9GP0YbQuNC4ICjQtNC+0YHRgtCw0YLQvtGH0L3QvtC1INGD0YHQu9C+0LLQuNC1INGB0LvQsNCx0L7QuSDQvtGC0LTQtdC70LjQvNC+0YHRgtC4KQoKIFct0LrQvtGA0YDQtdC70Y/RhtC40L7QvdC90LDRjyDQvNCw0YLRgNC40YbQsAogCmBgYHtyLCBmaWcud2lkdGg9NSwgZmlnLmhlaWdodD01fQpwbG90KHdjb3Ioc3NhLmxvbmcsIGdyb3VwcyA9IGFzLmxpc3QoMToxNSkpKQpgYGAK0JzQsNGC0YDQuNGG0LAg0L/RgNCw0LrRgtC40YfQtdGB0LrQuCDQtNC40LDQs9C+0L3QsNC70YzQvdCw0Y8gLS0g0YLRgNC10L3QtNGLINC4INGB0LXQt9C+0L3QvdGL0LUg0LrQvtC80L/QvtC90LXQvdGC0Ysg0L/RgNCw0LrRgtC40YfQtdGB0LrQuCB3LdC+0YDRgtC+0LPQvtC90LDQu9GM0L3RiwoK0J3QtdGCINGB0LjQu9GM0L3QvtC5INGA0LDQt9C00LXQu9C40LzQvtGB0YLQuCAtLSDQvtC00LjQvdCw0LrQvtCy0YvQtSDRgdC+0LHRgdGC0LLQtdC90L3Ri9C1INGH0LjRgdC70LAg0L7QutCw0LfQsNC70LjRgdGMINCyINGA0LDQt9C90YvRhSDQutC+0LzQv9C+0L3QtdC90YLQsNGFCgoqINCc0L7QttC90L4g0L/QvtGB0LzQvtGC0YDQtdGC0Ywg0L3QsCDQstC60LvQsNC00Ysg0LrQvtC80L/QvtC90LXQvdGCLCDRgdGC0YPQv9C10L3RjNC60Lgg0LHRg9C00YPRgiDRgdC+0L7RgtCy0LXRgtGB0YLQstC+0LLQsNGC0Ywg0LHQu9C40LfQutC40Lwg0YHQvtCx0YHRgtCy0LXQvdC90YvQvCDRh9C40YHQu9Cw0LwKCgojI1JlY29uc3RydWN0aW9uCgrQkiDQv9GA0LXQtNC/0L7Qu9C+0LbQtdC90LjQuCwg0YfRgtC+INGA0LDQvdCzINGA0Y/QtNCwIDgsINC/0L7Qu9GD0YfQuNC8CmBgYHtyLCBmaWcud2lkdGg9OCwgZmlnLmhlaWdodD04fQpsb25nLnJlY29uc3RydWN0ZWQgPC0gcmVjb25zdHJ1Y3Qoc3NhLmxvbmcsIGxpc3QoCiAgdHJlbmQgPSBjKDEsIDIpLAogIHBlcmlvZGljID0gMzo4KSkKcmVzLmxvbmcgPC0gcmVzaWR1YWxzKGxvbmcucmVjb25zdHJ1Y3RlZCkKcGFyKG1mcm93PWMoNCwxKSkKcGxvdChsb25nLCB0eXBlID0gJ2wnKQpwbG90KGxvbmcucmVjb25zdHJ1Y3RlZCR0cmVuZCwgdHlwZT0nbCcsIHlsYWIgPSAidHJlbmQiKQpwbG90KGxvbmcucmVjb25zdHJ1Y3RlZCRwZXJpb2RpYywgdHlwZT0nbCcsIHlsYWIgPSAicGVyaW9kaWMiKQpwbG90KHJlcy5sb25nLCB0eXBlPSdsJykKYGBgCgoK0JDQstGC0L7QutC+0LLQsNGA0LjQsNGG0LjQvtC90L3QsNGPINGE0YPQvdC60YbQuNGPCmBgYHtyLCBmaWcuaGVpZ2h0PTUsIGZpZy53aWR0aD0xMH0KYWNmKHJlcy5sb25nLCBsYWcubWF4ID0gNTApCmBgYArQqNGD0Lwg0L/QvtGF0L7QtiDQvdCwINCx0LXQu9GL0LkuCgojI9Ca0L7RgNC90Lgg0KXQnwoKTWluLW5vcm0g0JvQoNCkCgoqICRQX2kkIC0tINGB0YLQvtC70LHRhtGLINGC0YDQsNC10LrRgtC+0YDQvdC+0Lkg0LzQsNGC0YDQuNGG0YsKKiAkXHVuZGVybGluZXtQfV97aX0kIC0tINCy0LXQutGC0L7RgCAkUF9pJCDQsdC10Lcg0L/QvtGB0LvQtdC00L3QtdC5INC60L7QvtGA0LTQuNC90LDRgtGLCiogJFxwaV9pJCAtLSDQv9C+0YHQu9C10LTQvdGP0Y8g0LrQvtC+0YDQtNC40L3QsNGC0LAgJFBfaSQKCtCa0L7RjdGE0YTQuNGG0LjQtdC90YLRiyBtaW4tbm9ybSDQm9Cg0KQ6CgokXG1hdGhjYWx7Un0gPSBcZnJhY3sxfXsxLVxudV4yfVxzdW1cbGltaXRzX3tpPTF9XntkfXtccGlfaSBcdW5kZXJsaW5le1B9X3tpfX0kLCDQs9C00LUgJFxudV4yID0gXHBpXzFeMiArIOKApiArIFxwaV9kXjIkCgokeF97aSArIGR9ID0gXHN1bVxsaW1pdHNfe2sgPSAxfV5kYV9reF97aSArIGQgLSBrfSQsICRhX2QgXG5lcSAwJCAtLSDQvNC40L3QuNC80LDQu9GM0L3QsNGPINCb0KDQpCAKCiRQKFxtdSkgPSBcbXVeZCAtIFxzdW1cbGltaXRzX3trID0gMX1eZGFfa1xtdV57ZCAtIGt9JCAtLSDRhdCw0YDQsNC60YLQtdGA0LjRgdGC0LjRh9C10YHQutC40Lkg0L/QvtC70LjQvdC+0Lwg0JvQoNCkCgokXHtcbXVfalx9X3tqID0gMX1ecCQgLS0g0LrQvtGA0L3QuCDRhS7Qvy4g0LrRgNCw0YLQvdC+0YHRgtC4ICRrX2okLCAkXHN1bSBrX2ogPSBkJCwg0YLQvtCz0LTQsCAKCiR4X24gPSBcc3VtXGxpbWl0c197bSA9IDF9XnAoXHN1bVxsaW1pdHNfe2ogPSAwfV57a20gLSAxfSBDX3ttX2p9IG5eailcbXVfbV5uJAoKbWluLW5vcm0g0JvQoNCkINC4INC10LUg0LrQvtGA0L3QuCAo0L/RgNC10LTQv9C+0LvQsNCz0LDQtdC8LCDRh9GC0L4g0YDQsNC90LMg0YDRj9C00LAgOCkKYGBge3J9CmxyIDwtIGxycihzc2EubG9uZywgZ3JvdXBzID0gbGlzdCgxOjgpKQpwbG90KGxyKQpyIDwtIHJvb3RzKGxyKQpgYGAKCtCl0L7RgtC40Lwg0L/QvtGB0YLRgNC+0LjRgtGMINGE0L7RgNC80YPQu9GDINC00LvRjyDRgdC40LPQvdCw0LvQsCDRgNGP0LTQsCDRgSDRiNGD0LzQvtC8LgoK0JLQt9GP0LIg0LHQvtC70YzRiNC+0LUg0L7QutC90L4g0Lgg0L/QvtGB0YLRgNC+0LjQsiBtaW4tbm9ybSDQm9Cg0KQsINC90LDQudC00LXQvCDQvNC90L7Qs9C+INC60L7RgNC90LXQuSDRhS7Qvy4KCtCa0LDQuiDQstGL0LHRgNCw0YLRjCDQuNC3INC90LjRhSDRgtC1IHIg0LrQvtGA0L3QtdC5LCDRh9GC0L4g0L7Qv9GA0LXQtNC10LvRj9GO0YIg0YHRgtGA0YPQutGC0YPRgNGDINGB0LjQs9C90LDQu9CwPwoK0J3QsNC50LTQtdC8INGB0LjQs9C90LDQu9GM0L3Ri9C1INC60L7RgNC90LgsINCy0L7RgdC/0L7Qu9GM0LfQvtCy0LDQstGI0LjRgdGMINGD0YLQstC10YDQttC00LXQvdC40LXQvCwg0YfRgtC+INGDIG1pbi1ub3JtINCb0KDQpCDQstGB0LUg0LvQuNGI0L3QuNC1INC60L7RgNC90Lgg0L/QviDQvNC+0LTRg9C70Y4g0LzQtdC90YzRiNC1INC10LTQuNC90LjRhtGLLgpgYGB7cn0KbW9kcyA8LSBNb2QocikKcHJpbnQoclttb2RzID49IDFdKQpgYGAKCtCf0LXRgNC10LLQtdGA0L3QtdC8INGA0Y/QtApgYGB7cn0KbHIuYmFjayA8LSBscnIoc3NhLmxvbmcsIGdyb3VwcyA9IGxpc3QoMTo4KSwgcmV2ZXJzZSA9IFRSVUUpCnIuYmFjayA8LSByb290cyhsci5iYWNrKQptb2RzLmJhY2sgPC0gTW9kKHIuYmFjaykKcHJpbnQoci5iYWNrW21vZHMuYmFjayA+PSAxXSkKYGBgCtCc0L7QtNGD0LvQuCDQs9C70LDQstC90YvRhSDQutC+0YDQvdC10LkKYGBge3J9Cm1haW4ucm9vdHMgPC0gYyhyW01vZChyKSA+PSAxXSwgMS9yLmJhY2tbTW9kKHIuYmFjaykgPj0gMV0pCnByaW50KE1vZChtYWluLnJvb3RzKSkKYGBgCgrQodC+0L7RgtCy0LXRgtGB0YLQstGD0Y7RidC40LUg0L/QtdGA0LjQvtC00YsKYGBge3J9CnByaW50KDIqcGkvQXJnKG1haW4ucm9vdHMpKQpgYGAKCmBgYHtyLCBmaWcuaGVpZ2h0PSA1LCBmaWcud2lkdGg9NX0KcGxvdChtYWluLnJvb3RzLCB4bGltID0gYygtMSwxKSwgeWxpbSA9IGMoLTEsMSksIGFzcCA9IDEsIHhsYWIgPSAiUmUiLCB5bGFiID0gIkltIikKZHJhdy5jaXJjbGUoMCwwLDEsYm9yZGVyID0gJ3JlZCcpCmBgYAoK0J3QsNGI0LvQvtGB0Ywg0YLQvtC70YzQutC+IDYg0YHQuNCz0L3QsNC70YzQvdGL0YUg0LrQvtGA0L3QtdC5LCDQsCDQtNC+0LvQttC90L4g0LHRi9GC0YwgOC4KCtCf0L7Qv9GA0L7QsdGD0LXQvCDQtNGA0YPQs9C+0Lkg0YHQv9C+0YHQvtCxOiAKCtCh0L7RgdGC0LDQstC40Lwg0YHQuNGB0YLQtdC80YMg0Lgg0L3QsNC50LTQtdC8INC60L7RjdGE0YTQuNGG0LjQtdC90YLRiyDQv9C+INCc0J3QmgpgYGB7cn0KY29tcGxleC5MUyA8LSBmdW5jdGlvbihzZXJpZXMsIHNlcmllcy5yb290cyl7CiAgIFggPC0gbWFwcGx5KEZVTiA9IGZ1bmN0aW9uKHJvb3Qpe3Jvb3ReKDE6bGVuZ3RoKHNlcmllcykpfSwgc2VyaWVzLnJvb3RzKQogICBZIDwtIGNiaW5kKHNlcmllcykKICAgbGlzdChjb2VmID0gc29sdmUoQ29uaih0KFgpKSUqJVgpICUqJSBDb25qKHQoWCkpICUqJSBZLAogICAgICAgIFggPSBYKQp9CmNvZWZmcy5lc3RpbWF0ZXMgPC0gY29tcGxleC5MUyhsb25nLnJlY29uc3RydWN0ZWQkdHJlbmQgKyBsb25nLnJlY29uc3RydWN0ZWQkcGVyaW9kaWMsIHIpCmBgYAoK0JLQvtGB0L/QvtC70YzQt9GD0LXQvNGB0Y8g0YLQtdC8LCDRh9GC0L4g0L/QtdGA0LXQtCDRgdC40LPQvdCw0LvRjNC90YvQvNC4INC60L7RgNC90Y/QvNC4INC00L7Qu9C20L3RiyDRgdGC0L7Rj9GC0Ywg0LzQsNC60YHQuNC80LDQu9GM0L3Ri9C1INC60L7RjdGE0YTQuNGG0LjQtdC90YLRiwpgYGB7cn0KaWR4LnJvb3RzIDwtb3JkZXIoTW9kKGNvZWZmcy5lc3RpbWF0ZXMkY29lZiksIGRlY3JlYXNpbmcgPSBUUlVFKVsxOjhdCm1haW4ucm9vdHM8LXJbaWR4LnJvb3RzXQpyZXMuY29lZmZzIDwtIGNvZWZmcy5lc3RpbWF0ZXMkY29lZltpZHgucm9vdHNdCmBgYAoKCtCc0L7QtNGD0LvQuCDQs9C70LDQstC90YvRhSDQutC+0YDQvdC10LkKYGBge3J9CnByaW50KE1vZChtYWluLnJvb3RzKSkKYGBgCgrQodC+0L7RgtCy0LXRgtGB0YLQstGD0Y7RidC40LUg0L/QtdGA0LjQvtC00YsKYGBge3J9CnByaW50KDIqcGkvQXJnKG1haW4ucm9vdHMpKQpgYGAKCgpgYGB7ciwgZmlnLmhlaWdodD0gNSwgZmlnLndpZHRoPTV9CnBsb3QobWFpbi5yb290cywgeGxpbSA9IGMoLTEsMSksIHlsaW0gPSBjKC0xLDEpLCBhc3AgPSAxLCB4bGFiID0gIlJlIiwgeWxhYiA9ICJJbSIpCmRyYXcuY2lyY2xlKDAsMCwxLGJvcmRlciA9ICdyZWQnKQpgYGAKCmBgYHtyfQpyZWxhdGlvbiA8LSBmdW5jdGlvbihuKXsKICByYmluZChyZXMuY29lZmZzKSAlKiUgdChtYXBwbHkoRlVOID0gZnVuY3Rpb24ocm9vdCl7cm9vdF5ufSwgbWFpbi5yb290cykpCn0KYGBgCgoKYGBge3J9CnBsb3QobG9uZy5yZWNvbnN0cnVjdGVkJHRyZW5kICsgbG9uZy5yZWNvbnN0cnVjdGVkJHBlcmlvZGljLCB0eXBlID0gJ2wnLCB5bGFiID0gInNlcmllcyIpCmxpbmVzKHggPSAxOjEwMDAsIHkgPSBSZShyZWxhdGlvbigxOjEwMDApKSwgY29sID0gJ3JlZCcpCmBgYAoKI1Nob3J0IHNlcmllcwoKYGBge3J9CnBsb3Qoc2hvcnQsIHR5cGU9J2wnKQpgYGAKYGBge3J9CnNwZWMucGdyYW0oc2hvcnQsIGRldHJlbmQgPSBUUlVFLCBmYXN0ID0gRkFMU0UsIGxvZz0nbm8nLCB0YXBlcj0wLCB4YXh0ID0gJ24nKQpsIDwtIHNlcSgwLCAwLjUsIDAuMDUpCmF4aXMoMSwgYXQgPSBsLCBsYWJlbHMgPSBsKQpgYGAK0J7QttC40LTQsNC10LwsINGH0YLQviDRgNCw0L3QsyDRgNGP0LTQsCDQutCw0Log0LzQuNC90LjQvNGD0LwgNCAo0YLRgNC10L3QtCArINGB0LjQvdGD0YEpLgoKRGVjb21wb3NpdGlvbgpgYGB7cn0Kc2hvcnQuc3NhIDwtIHNzYShzaG9ydCwga2luZD0nMWQtc3NhJykKYGBgCgrQodC+0LHRgdGC0LLQtdC90L3Ri9C1INGH0LjRgdC70LAKYGBge3J9CnBsb3Qoc2hvcnQuc3NhKQpgYGAKCgoKCtCh0L7QsdGB0YLQstC10L3QvdGL0LUg0LLQtdC60YLQvtGA0LA6CmBgYHtyLCBmaWcud2lkdGg9OCwgZmlnLmhlaWdodD04fQpwbG90KHNob3J0LnNzYSwgdHlwZT0ndmVjdG9ycycsIGlkeCA9IDE6MjApCmBgYAogCgpgYGB7ciwgZmlnLndpZHRoPSA4LCBmaWcuaGVpZ2h0PSA4fQpwbG90KHNob3J0LnNzYSwgdHlwZT0ncGFpcmVkJywgaWR4PSBjKDE6MjApKQpgYGAKCmBgYHtyfQpwYXJlc3RpbWF0ZShzaG9ydC5zc2EsIGdyb3VwcyA9IGxpc3QoMzo0LCA1OjYsIDc6OCwgMTE6MTIsIDEzOjE0KSkKYGBgCgoKIApgYGB7ciwgZmlnLndpZHRoPTUsIGZpZy5oZWlnaHQ9NX0KcGxvdCh3Y29yKHNob3J0LnNzYSwgZ3JvdXBzID0gYXMubGlzdCgxOjIwKSkpCmBgYAoKYGBge3IsIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTh9CnNob3J0LnJlY29uc3RydWN0ZWQgPC0gcmVjb25zdHJ1Y3Qoc2hvcnQuc3NhLCBsaXN0KAogIHRyZW5kID0gYygxLCAyKSwKICBwZXJpb2RpYyA9IGMoMzo4LDExOjE0KSkpCnJlcy5zaG9ydCA8LSByZXNpZHVhbHMoc2hvcnQucmVjb25zdHJ1Y3RlZCkKcGFyKG1mcm93PWMoNCwxKSkKcGxvdChzaG9ydCwgdHlwZSA9ICdsJykKcGxvdChzaG9ydC5yZWNvbnN0cnVjdGVkJHRyZW5kLCB0eXBlPSdsJywgeWxhYiA9ICJ0cmVuZCIpCnBsb3Qoc2hvcnQucmVjb25zdHJ1Y3RlZCRwZXJpb2RpYywgdHlwZT0nbCcsIHlsYWIgPSAicGVyaW9kaWMiKQpwbG90KHJlcy5zaG9ydCwgdHlwZT0nbCcpCmBgYApgYGB7ciwgZmlnLmhlaWdodD01LCBmaWcud2lkdGg9MTB9CmFjZihyZXMuc2hvcnQsIGxhZy5tYXggPSA1MCkKYGBgCgojI1Byb2plY3Rpb24gU1NBCgrQlNCy0L7QudC90L7QtSDRhtC10L3RgtGA0LjRgNC+0LLQsNC90LjQtQoKJFxtYXRoY2Fse0F9KFxtYXRoYmZ7WH0pID0gW1xtYXRoY2Fse0V9XzEoXG1hdGhiZntYfSkgOiBcbGRvdHMgOiBcbWF0aGNhbHtFfV8xKFxtYXRoYmZ7WH0pXSQsINCz0LTQtSAkXG1hdGhjYWx7RX1fMShcbWF0aGJme1h9KSQgLS0g0LLQtdC60YLQvtGAINC40Lcg0YHRgNC10LTQvdC40YUg0L/QviDRgdGC0YDQvtC60LDQvAoKJFxtYXRoY2Fse0J9KFxtYXRoYmZ7WH0pID0gW1xtYXRoY2Fse0V9X3sxMn0oXG1hdGhiZntYfSkgOiBcbGRvdHMgOiBcbWF0aGNhbHtFfV97MTJ9KFxtYXRoYmZ7WH0pXSQsINCz0LTQtSAkXG1hdGhjYWx7RX1fezEyfShcbWF0aGJme1h9KSQgLS0g0LLQtdC60YLQvtGALCDQsiDQutC+0YLQvtGA0L7QvCBqLdCw0Y8g0LrQvtC80L/QvtC90LXQvdGC0LAg0YDQsNCy0L3QsCDRgdGA0LXQtNC90LXQvNGDINC60L7QvNC/0L7QvdC10L3RgiAkWF9qXnsoYyl9ID0gWF9pIC0gXG1hdGhjYWx7RX1fMShcbWF0aGJme1h9KSQuCgrQn9GA0Lgg0LTQstC+0LnQvdC+0Lwg0YbQtdC90YLRgNC40YDQvtCy0LDQvdC40Lgg0L/RgNC40LzQtdC90Y/QtdC8IFNWRCDQuiDQvNCw0YLRgNC40YbQtSAkXG1hdGhiZntBfSA9IFxtYXRoY2Fse0F9KFxtYXRoYmZ7WH0pICsgXG1hdGhjYWx7Qn0oXG1hdGhiZntYfSkkCgrQn9C+0LvRg9GH0LjQvCDRgNCw0LfQu9C+0LbQtdC90LjQtSAkXG1hdGhiZntYfSA9IFxtYXRoYmZ7QX0gKyBcc3VtXGxpbWl0c197aSA9IDF9XmRcc3FydHtcbGFtYmRhX2l9VV9pVl9pXlxtYXRocm17VH0kLCDQs9C00LUgJChcbGFtYmRhX2ksIFVfaSwgVl9pKSQg0YHQvtC+0YLQstC10YLRgdGC0LLRg9GO0YIgJFNeKiA9IChcbWF0aGJme1h9IC0gXG1hdGhiZntBfSkoXG1hdGhiZntYfSAtIFxtYXRoYmZ7QX0pXlxtYXRocm17VH0kLgoKCtCi0LDQuiDQutCw0Log0LjRgdGF0L7QtNC90YvQuSDRgtGA0LXQvdC0INC70LjQvdC10LnQvdGL0LksINC00LLQvtC50L3QvtC1INGG0LXQvdGC0YDQuNGA0L7QstCw0L3QuNC1INC/0L7QvNC+0LbQtdGCINC+0YLQtNC10LvQuNGC0Ywg0LvQuNC90LXQudC90YPRjiDRh9Cw0YHRgtGMINC+0YIg0LrQvtC70LXQsdCw0L3QuNC5ICjQu9C40L3QtdC50L3QsNGPINGH0LDRgdGC0Ywg0L/QvtC/0LDQtNC10YIg0LIgJFxtYXRoYmZ7QX0kKS4KCmBgYHtyfQpzaG9ydC5wcm9qZWN0aW9uLnNzYSA8LSBzc2EoeCA9IHNob3J0LCBjb2x1bW4ucHJvamVjdG9yID0gJ2NlbnRlcmluZycsIHJvdy5wcm9qZWN0b3IgPSAnY2VudGVyaW5nJykKYGBgCgpgYGB7ciwgZmlnLmhlaWdodD01LCBmaWcud2lkdGg9MTB9CnBsb3Qoc2hvcnQucHJvamVjdGlvbi5zc2EpCnBsb3Qoc2hvcnQucHJvamVjdGlvbi5zc2EsIHR5cGU9J3ZlY3RvcnMnLCBpZHggPSAxOjIwKQpgYGAKCtCf0L4g0LLQuNC00YMg0YHQvtCx0YHRgtCy0LXQvdC90YvRhSDQstC10LrRgtC+0YDQvtCyINCy0LjQtNC90L4sINGH0YLQviDRgtGA0LXQvdC0INC+0YLQtNC10LvQuNC70YHRjyDQu9GD0YfRiNC1LgoKYGBge3J9CnBsb3Qoc2hvcnQucHJvamVjdGlvbi5zc2EsIHR5cGU9J3BhaXJlZCcsIGlkeD0gYygxOjIwKSkKcGFyZXN0aW1hdGUoc2hvcnQucHJvamVjdGlvbi5zc2EsIGdyb3VwcyA9IGxpc3QoMzo0LCA1OjYsIDc6OCwgMTE6MTIsIDEzOjE0KSwgbWV0aG9kID0iZXNwcml0LWxzIikKYGBgCgoKYGBge3IsIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTh9CnNob3J0LnJlY29uc3RydWN0ZWQgPC0gcmVjb25zdHJ1Y3Qoc2hvcnQucHJvamVjdGlvbi5zc2EsIGxpc3QoCiAgdHJlbmQgPSBjKDEsIDIpLAogIHBlcmlvZGljID0gYygzOjgsMTE6MTQpKSkKcmVzLnByb2plY3Rpb24gPC0gcmVzaWR1YWxzKHNob3J0LnJlY29uc3RydWN0ZWQpCnBhcihtZnJvdz1jKDQsMSkpCnBsb3Qoc2hvcnQsIHR5cGUgPSAnbCcpCnBsb3Qoc2hvcnQucmVjb25zdHJ1Y3RlZCR0cmVuZCwgdHlwZT0nbCcsIHlsYWIgPSAidHJlbmQiKQpwbG90KHNob3J0LnJlY29uc3RydWN0ZWQkcGVyaW9kaWMsIHR5cGU9J2wnLCB5bGFiID0gInBlcmlvZGljIikKcGxvdChyZXMucHJvamVjdGlvbiwgdHlwZT0nbCcpCmBgYAoKYGBge3IsIGZpZy5oZWlnaHQ9NSwgZmlnLndpZHRoPTEwfQphY2YocmVzLnByb2plY3Rpb24sIGxhZy5tYXggPSA1MCkKYGBgCtCh0YLQsNC70L4g0L3QtdC80L3QvtCz0L4g0LvRg9GH0YjQtSwg0L3QviDQstGB0LUt0YLQsNC60Lgg0YfRgtC+LdGC0L4g0L7RgdGC0LDQu9C+0YHRjC4KCiNUb2VwbGl0eiBTU0EgdnMgQmFzaWMgU1NBCgrQlNC70Y8g0LrQvtGA0L7RgtC60LjRhSDRgNGP0LTQvtCyLCDQutC+0YLQvtGA0YvQtSDQv9GA0LXQtNC/0L7Qu9Cw0LPQsNGO0YLRgdGPINGB0YLQsNGG0LjQvtC90LDRgNC90YvQvNC4INGA0LXQutC+0LzQtdC90LTRg9C10YLRgdGPINC30LDQvNC10L3Rj9GC0Ywg0LzQsNGC0YDQuNGG0YMgJFxtYXRoYmZ7U30gPSBcbWF0aGJme1h9XG1hdGhiZntYfV5cbWF0aHJte1R9JCDQvdCwINCw0LLRgtC+0LrQvtCy0LDRgNC40LDRhtC40L7QvdC90YPRjiDQvNCw0YLRgNC40YbRgyAkXHdpZGV0aWxkZXtcbWF0aGJme0N9fSQg0L3QsCDRjdGC0LDQv9C1INGA0LDQt9C70L7QttC10L3QuNGPCgoKJFx3aWRldGlsZGV7Y31fe2lqfSA9IFxmcmFjezF9e04gLSB8aSAtIGp8fSBcc3VtXGxpbWl0c197bSA9IDF9XntOIC0gfGkgLSBqfH0geF9tIHhfe20gKyB8aSAtIGp8fSQsICQxIFxsZXEgaSwgaiBcbGVxIEwkLgoK0JfQsNC80LXRh9Cw0L3QuNC1OgoK0J/QvtC70YPRh9C10L3QvdC+0LUg0YDQsNC70L7QttC10L3QuNC1ICRcbWF0aGJie1h9ID0gXHN1bVxsaW1pdHNfe2kgPSAxfV5MXHNpZ21hX2lQX2lRX2leXG1hdGhybXtUfSQsINCz0LTQtSAkUF9pJCAtLSDRgS7Qsi4gJFx3aWRldGlsZGV7XG1hdGhiZntDfX0kLCDQvtCx0YDQsNC30YPRjtGCINC+0YDRgtC+0L3QvtGA0LzQuNGA0L7QstCw0L3QvdGL0Lkg0LHQsNC30LjRgSDQsiAkXG1hdGhiYntSfSQsICRRX2kgPSBcZnJhY3tcbWF0aGJie1h9XlxtYXRocm17VH1QX2l9e1x8XG1hdGhiYntYfV5cbWF0aHJte1R9UF9pXHx9JC4g0K3RgtC+INGA0LDQt9C70L7QttC10L3QuNC1INC90LUg0Y/QstC70Y/QtdGC0YHRjyDRgdC40L3Qs9GD0LvRj9GA0L3Ri9C8INGA0LDQt9C70L7QttC10L3QuNC10LwsICRcc2lnbWFfaSA9IFx8XG1hdGhiYntYfV5cbWF0aHJte1R9UF9pXHwkINCy0L7QvtCx0YnQtSDQs9C+0LLQvtGA0Y8g0L3QtSDRgdC+0LHRgdGC0LLQtdC90L3Ri9C1INGH0LjRgdC70LAgJFx3aWRldGlsZGV7XG1hdGhiZntDfX0kLgoKCtCf0YDQuNC80LXRgDog0YDRj9C0ICR4X24gPSBlXntcYWxwaGEgbn0gY29zKDJccGkgbi83KSArIFxlcHNpbG9uX24kCmBgYHtyfQpzZXQuc2VlZCgxMDApCmVwcyA8LSBybm9ybSgxMDApCmFscGhhIDwtIHNlcSgwLjAwMDEsIDAuMDEsIDAuMDAwMSkKeCA8LSBtYXBwbHkoRlVOID0gZnVuY3Rpb24oYSkge2V4cChhICogKDE6MTAwKSkgKiBjb3MoMipwaSooMToxMDApLzcpICsgZXBzfSwgYWxwaGEpCgpiYXNpYyA8LSBhcHBseSh4LCAyLCBGVU4gPSBmdW5jdGlvbihzZXJpZXMpewogIHMgPC0gc3NhKHNlcmllcykKICByZWMgPC0gcmVjb25zdHJ1Y3QocywgZ3JvdXBzID0gbGlzdCh0cmVuZCA9IGMoMSwgMikpKQogIHBlciA8LSBzZXJpZXMgLSBlcHMKICBtc2UgPC0gc3VtKChyZWMkdHJlbmQgLSBwZXIpXjIpL2xlbmd0aChwZXIpCn0pCgp0ZW9wbGl0eiA8LSBhcHBseSh4LCAyLCBGVU4gPSBmdW5jdGlvbihzZXJpZXMpewogIHMgPC0gc3NhKHNlcmllcywga2luZCA9ICJ0b2VwbGl0ei1zc2EiKQogIHJlYyA8LSByZWNvbnN0cnVjdChzLCBncm91cHMgPSBsaXN0KHRyZW5kID0gYygxLCAyKSkpCiAgcGVyIDwtIHNlcmllcyAtIGVwcwogIG1zZSA8LSBzdW0oKHJlYyR0cmVuZCAtIHBlcileMikvbGVuZ3RoKHBlcikKfSkKYGBgCmBgYHtyfQpwbG90KHkgPSB0ZW9wbGl0eiwgeCA9IGFscGhhLCB0eXBlID0gImwiLCB5bGFiID0gIk1TRSIsIHhsYWIgPSAnYWxwaGEnLCBtYWluID0gIlRlb3BsaXR6IHZzIEJhc2ljIFNTQSIpCmxpbmVzKHkgPSBiYXNpYywgIHggPSBhbHBoYSwgIGNvbCA9ICdyZWQnKQpsZWdlbmQoInRvcGxlZnQiLCBjKCJUZW9wbGl0eiIsICJCYXNpYyIpLCBsdHk9YygxLDEpLCBsd2Q9YygyLjUsMi41KSxjb2w9YygiYmxhY2siLCJyZWQiKSkKYGBgCgoj0J/QvtGB0LvQtdC00L7QstCw0YLQtdC70YzQvdGL0Lkg0LDQvdCw0LvQuNC3INGA0LXQsNC70YzQvdC+0LPQviDRgNGP0LTQsApgYGB7cn0KdWsgPC0gcmVhZC5jc3YoIlVLLmNzdiIsIHNlcCA9ICcsJywgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQp1ayA8LSB0cyh1ayRFWFBFTkQsIHN0YXJ0ID0gYygxOTgwLCAxKSwgZnJlcXVlbmN5ID0gMTIpCgpkZi51ayA8LSBkYXRhLmZyYW1lKHNlcS5EYXRlKGZyb20gPSBhcy5EYXRlKCIxOTgwLTAxLTAxIiksIHRvID0gYXMuRGF0ZSgiMjAxNi0xMi0wMSIpLCBieSA9ICJtb250aCIpLCB1aykKY29sbmFtZXMoZGYudWspIDwtIGMoIkRBVEUiLCAiRVhQIikKaGVhZCh1aywgMjQpCmBgYApgYGB7cn0KcGxvdCh1aykKYGBgCgoK0J/QtdGA0LjQvtC00L7Qs9GA0LDQvNC80LAg0YDRj9C00LAKYGBge3J9CnNwZWMucGdyYW0odWssIGRldHJlbmQgPSBUUlVFLCBmYXN0ID0gRkFMU0UsIGxvZz0nbm8nLCB4YXh0ID0gJ24nKQpheGlzKDEsIGF0ID0gYygwLDEsMiwzLDQsNSw2KSwgbGFiZWxzID0gYygnMCcsICcxLzEyJywgJzIvMTInLCAnMy8xMicsICc0LzEyJywgJzUvMTInLCAnNi8xMicpKQpgYGAKCiMj0KHQs9C70LDQttC40LLQsNC90LjQtQoKKiDQotGA0LXQvdC0INGB0LvQvtC20L3QvtC5INGE0L7RgNC80YssINCy0L7Qt9C80L7QttC90L4sINC90LUg0L7Qv9C40YHRi9Cy0LDQtdGC0YHRjyDRgNGP0LTQvtC8INC60L7QvdC10YfQvdC+0Lkg0YDQsNC30LzQtdGA0L3QvtGB0YLQuAoqINCV0YHQu9C4INCy0YvQsdC10YDQtdC8INCx0L7Qu9GM0YjQvtC1INC+0LrQvdC+LCDQutC+0LzQv9C+0L3QtdC90YLRiyDRgtGA0LXQvdC00LAg0YHQvNC10YjQsNGO0YLRgdGPINGBINC/0LXRgNC40L7QtNC40YfQtdGB0LrQuNC80LgKKiDQktC+0LfRjNC80LXQvCDQvNCw0LvQtdC90YzQutC+0LUg0L7QutC90L46INGF0L7RgNC+0YjQviDQstGL0LTQtdC70LjQvCDRgtGA0LXQvdC0LCDQvdC+INGB0LzQtdGI0LDRjtGC0YHRjyDQvtGB0YLQsNC70YzQvdGL0LUg0LrQvtC80L/QvtC90LXQvdGC0YsKKiDQoNC10YjQtdC90LjQtTog0YHQvdCw0YfQsNC70LAg0LLRi9C00LXQu9C40Lwg0YLRgNC10L3QtCDRgSDQvNCw0LvQtdC90YzQutC40Lwg0L7QutC90L7QvCwg0L/QvtGC0L7QvCDQv9GA0LjQvNC10L3QuNC8IFNTQSDRgSDQsdC+0LvRjNGI0LjQvCDQvtC60L3QvtC8INC6INC+0YHRgtCw0YLQutGDLgoK0JLRi9Cx0LjRgNCw0LXQvCDQvdC10LHQvtC70YzRiNGD0Y4g0LTQu9C40L3RgyDQvtC60L3QsCDQutGA0LDRgtC90YPRjiDQv9C10YDQuNC+0LTRgyDRgdC10LfQvtC90L3QvtC5INC60L7QvNC/0L7QvdC10L3RgtGLLCBMID0gMjQuCmBgYHtyfQp1ay5zc2EgPC0gc3NhKHVrLCBMID0gMjQpCmBgYAoK0JLQutC70LDQtNGLCmBgYHtyfQpwbG90KHVrLnNzYSkKYGBgCgrQktC40LTQvdC+LCDRh9GC0L4g0YHQvtCx0YHRgtCy0LXQvdC90YvQtSDRh9C40YHQu9CwINC/0LXRgNC40L7QtNC40YfQtdGB0LrQuNGFINC60L7QvNC/0L7QvdC10L3RgiDQsdC70LjQt9C60LjQtS4g0KHQutC+0YDQtdC1INCy0YHQtdCz0L4g0Y3RgtC4INC60L7QvNC/0L7QvdC10L3RgtGLINGB0LzQtdGI0LDRjtGC0YHRjyAo0L/RgNC+0LHQu9C10LzQsCDRgSDRgdC40LvRjNC90L7QuSDRgNCw0LfQtNC10LvQuNC80L7RgdGC0YzRjikuCgoK0KHQvtCx0YHRgtCy0LXQvdC90YvQtSDQstC10LrRgtC+0YDQsApgYGB7cn0KcGxvdCh1ay5zc2EsIHR5cGU9InZlY3RvcnMiLCBpZHg9MToxMikKcGxvdCh1ay5zc2EsIHR5cGU9InBhaXJlZCIsIGlkeD0xOjExKQpgYGAKIyPQktC+0YHRgdGC0LDQvdC+0LLQu9C10L3QvdGL0LUg0L/QviDRjdC70LXQvNC10L3RgtCw0YDQvdGL0Lwg0LrQvtC80L/QvtC90LXQvdGC0LDQvApgYGB7cn0KcGxvdCh1ay5zc2EsIHR5cGUgPSdzZXJpZXMnLCBncm91cHMgPSBhcy5saXN0KDE6MTIpKQpgYGAK0J/QtdGA0LLQsNGPINGN0LvQtdC80LXQvdGC0LDRgNC90LDRjyDQutC+0LzQv9C+0L3QtdC90YLQsCDRgdC+0L7RgtCy0LXRgtGB0YLQstGD0LXRgiDRgtGA0LXQvdC00YMsINC00LDQu9GM0YjQtSDQuNC00YPRgiDQv9C10YDQuNC+0LTQuNC60LguCgojI9CS0YvQtNC10LvQtdC90LjQtSDRgtGA0LXQvdC00LAKCmBgYHtyfQp1ay50cmVuZCA8LSByZWNvbnN0cnVjdCh1ay5zc2EsIGdyb3VwcyA9IGxpc3QoMSkpCnBsb3QodWspCmxpbmVzKHVrLnRyZW5kJEYxLCBjb2wgPSAncmVkJykKdWsuZGV0cmVuZGVkIDwtIHJlc2lkdWFscyh1ay50cmVuZCkKYGBgCtCf0LXRgNC40L7QtNC+0LPRgNCw0LzQvNCwINC+0YHRgtCw0YLQutCwCmBgYHtyfQpzcGVjLnBncmFtKHVrLmRldHJlbmRlZCwgZGV0cmVuZCA9IEZBTFNFLCBmYXN0ID0gRkFMU0UsIGxvZz0nbm8nLCB4YXh0ID0gJ24nKQpheGlzKDEsIGF0ID0gYygwLDEsMiwzLDQsNSw2KSwgbGFiZWxzID0gYygnMCcsICcxLzEyJywgJzIvMTInLCAnMy8xMicsICc0LzEyJywgJzUvMTInLCAnNi8xMicpKQpgYGAK0J7QttC40LTQsNC10LwsINGH0YLQviDQtNCw0LvRjNGI0LUg0LHRg9C00LXRgiDQtNC+0YHRgtCw0YLQvtGH0L3QviDQstGL0LTQtdC70LjRgtGMIDkg0LrQvtC80L/QvtC90LXQvdGCICjRgdC40L3Rg9GBINGBINC/0LXRgNC40L7QtNC+0LwgMiDQuNC80LXQtdGCINGA0LDQvdCzIDEpLgoKIyPQodC10LfQvtC90L3QvtGB0YLRjAoK0JLQvtC30YzQvNC10Lwg0LzQsNC60YHQuNC80LDQu9GM0L3QvtC1ICRMIFxsZXEgTi8yJCDQutGA0LDRgtC90L7QtSAxMiwg0YIu0LUuIDIxNiAo0LTQu9C40L3QsCDRgNGP0LTQsCA0NDQpLgoKYGBge3J9CnVrLmRldHJlbmRlZC5zc2EgPC0gc3NhKHVrLmRldHJlbmRlZCwgTD0yMTYpCnBsb3QodWsuZGV0cmVuZGVkLnNzYSkKYGBgCtCS0LjQtNC90L4g0YHRgtGD0L/QtdC90YzQutC4LCDQutC+0YLQvtGA0YvQtSwg0LLQvtC30LzQvtC20L3Qviwg0YHQvtC+0YLQstC10YLRgdGC0LLRg9GO0YIg0YHQuNC90YPRgdC+0LjQtNCw0LwuCgoKYGBge3IsIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTh9CnBsb3QodWsuZGV0cmVuZGVkLnNzYSwgdHlwZSA9ICJwYWlyZWQiLCBpZHggPSAxOjMwLCBwbG90LmNvbnRyaWIgPSBGQUxTRSkKYGBgCtCX0LDQvNC10YLQvdGLINC60L7QvNC/0L7QvdC10L3RgtGLLCDRgdC+0L7RgtCy0LXRgtGB0YLQstGD0Y7RidC40LUg0L/QtdGA0LjQvtC00LDQvCAxMiwgNiwgNCDQuCAyLjQuCgoK0JrQvtC80L/QvtC90LXQvdGC0LAsINGB0L7QvtGC0LLQtdGC0YHRgtCy0YPRjtGJ0LDRjyDQv9C10YDQuNC+0LTRgyAyLgpgYGB7cn0KcGxvdCh1ay5kZXRyZW5kZWQuc3NhLCB0eXBlID0gInZlY3RvcnMiLCBpZHggPSA5LCBwbG90LmNvbnRyaWIgPSBGQUxTRSkKYGBgCgrQn9GA0L7QstC10YDQuNC8CmBgYHtyfQpwYXJlc3RpbWF0ZSh1ay5kZXRyZW5kZWQuc3NhLCBncm91cHMgPSBsaXN0KDE6OSksIG1ldGhvZCA9ICJlc3ByaXQtbHMiKQpgYGAKCgpXLdC60L7RgNGA0LXQu9GP0YbQuNC+0L3QvdCw0Y8g0LzQsNGC0YDQuNGG0LAKCmBgYHtyLCBmaWcud2lkdGg9NSwgZmlnLmhlaWdodD01fQpwbG90KHdjb3IodWsuZGV0cmVuZGVkLnNzYSwgZ3JvdXBzID0gYXMubGlzdCgxOjIwKSkpCmBgYAoKYGBge3J9CnVrLnNlYXNvbmFsaXR5IDwtIHJlY29uc3RydWN0KHVrLmRldHJlbmRlZC5zc2EsIGdyb3VwcyA9IGxpc3QoMTo5KSkKcmVzMSA8LSByZXNpZHVhbHModWsuc2Vhc29uYWxpdHkpCmBgYApgYGB7ciwgZmlnLmhlaWdodD0xMCwgZmlnLndpZHRoPTh9CnBhcihtZnJvdz1jKDQsMSkpCnBsb3QodWssIHR5cGUgPSAnbCcsIHlsYWI9J09yaWdpbmFsJykKcGxvdCh1ay50cmVuZCRGMSwgdHlwZT0nbCcsIHlsYWI9J1RyZW5kJykKcGxvdCh1ay5zZWFzb25hbGl0eSRGMSwgdHlwZT0nbCcsIHlsYWIgPSAnU2Vhc29uYWwgQ29tcG9uZW50JykKcGxvdChyZXMxLCB0eXBlPSdsJywgeWxhYj0nUmVzaWR1YWxzJykKYGBgCtCSINC+0YHRgtCw0YLQutC1INC+0YHRgtCw0LvRgdGPINGC0YDQtdC90LQuCgoKYGBge3J9CmFjZihyZXMxKQpzcGVjLnBncmFtKHJlczEsIGRldHJlbmQgPSBGQUxTRSwgZmFzdCA9IEZBTFNFLCBsb2c9J25vJywgeGF4dCA9ICduJykKYXhpcygxLCBhdCA9IGMoMCwxLDIsMyw0LDUsNiksIGxhYmVscyA9IGMoJzAnLCAnMS8xMicsICcyLzEyJywgJzMvMTInLCAnNC8xMicsICc1LzEyJywgJzYvMTInKSkKYGBgCgojI9Ce0YbQtdC90LrQsCDQtNC40YHQv9C10YDRgdC40Lgg0YjRg9C80LAKYGBge3J9CnNpZ21hLm4uc3FyIDwtIHNzYShyZXMxXjIsIEwgPSAzMCkKc2lnbWEubiA8LSBzcXJ0KHJlY29uc3RydWN0KHNpZ21hLm4uc3FyLCBncm91cHMgPSBsaXN0KDEpKSRGMSkKcGxvdChyZXMxLCB0eXBlPSdsJykKbGluZXMoc2lnbWEubiwgdHlwZSA9ICdsJywgY29sPSdibHVlJykKbGluZXMoLXNpZ21hLm4sIHR5cGU9J2wnLCBjb2w9J2JsdWUnKQpgYGAKCiNGb3JlY2FzdGluZwoK0KDQsNGB0YHQvNC+0YLRgNC40Lwg0YLQvtGCINC20LUg0YHQsNC80YvQuSDRgNGP0LQsINC90L4g0LHQtdC3INC/0L7RgdC70LXQtNC90LjRhSDRiNC10YHRgtC4INC70LXRgi4KYGBge3J9CnVrLnRyYWluIDwtIGhlYWQodWssIC03MikKcGxvdCh1ay50cmFpbikKdWsudHJhaW4uc3NhIDwtIHNzYSh1ay50cmFpbiwgTCA9IDI0KQp1ay50cmFpbi50cmVuZCA8LSByZWNvbnN0cnVjdCh1ay50cmFpbi5zc2EsIGdyb3VwcyA9IDEpCnVrLnRyYWluLmRldHJlbmRlZCA8LSByZXNpZHVhbHModWsudHJhaW4udHJlbmQpCnVrLnRyYWluLmRldHJlbmRlZC5zc2EgPC0gc3NhKHVrLnRyYWluLmRldHJlbmRlZCwgTCA9IDE4MCkKdWsudHJhaW4uc2Vhc29uYWxpdHkgPC0gcmVjb25zdHJ1Y3QodWsudHJhaW4uZGV0cmVuZGVkLnNzYSwgZ3JvdXBzID0gbGlzdCgxOjkpKQpgYGAKCgojI1ItZm9yZWNhc3RpbmcKCtCf0YDQvtC00L7Qu9C20LDQtdC8INGA0Y/QtCDRgSDQv9C+0LzQvtGJ0YzRjiBtaW4tbm9ybSBMUlIuCmBgYHtyfQp1ay50cmVuZC5mb3JlY2FzdCA8LSBwcmVkaWN0KHVrLnRyYWluLnNzYSwgbWV0aG9kID0gInJlY3VycmVudCIsIGxlbiA9IDcyLCBncm91cHMgPSAxKQp1ay5zZWFzb25hbGl0eS5mb3JlY2FzdCA8LSBwcmVkaWN0KHVrLnRyYWluLmRldHJlbmRlZC5zc2EsIG1ldGhvZCA9ICJyZWN1cnJlbnQiLCBsZW4gPSA3MiwgZ3JvdXBzID0gbGlzdCgxOjkpKQpwbG90KHVrLCB4bGltID0gYygyMDAwLCAyMDIwKSkKbGluZXModWsudHJlbmQuZm9yZWNhc3QgKyB1ay5zZWFzb25hbGl0eS5mb3JlY2FzdCwgY29sID0gJ3JlZCcpCmBgYApNU0UKYGBge3J9CnN1bSgodGFpbCh1aywgNzIpIC0gKHVrLnRyZW5kLmZvcmVjYXN0ICsgdWsuc2Vhc29uYWxpdHkuZm9yZWNhc3QpKV4yKS83MgpgYGAKCiMjVi1mb3JlY2FzdGluZwoKYGBge3J9CnVrLnRyZW5kLmZvcmVjYXN0IDwtIHByZWRpY3QodWsudHJhaW4uc3NhLCBtZXRob2QgPSAidmVjdG9yIiwgbGVuID0gNzIsIGdyb3VwcyA9IDEpCnVrLnNlYXNvbmFsaXR5LmZvcmVjYXN0IDwtIHByZWRpY3QodWsudHJhaW4uZGV0cmVuZGVkLnNzYSwgbWV0aG9kID0gInZlY3RvciIsIGxlbiA9IDcyLCBncm91cHMgPSBsaXN0KDE6OSkpCnBsb3QodWssIHhsaW0gPSBjKDIwMDAsIDIwMjApKQpsaW5lcyh1ay50cmVuZC5mb3JlY2FzdCArIHVrLnNlYXNvbmFsaXR5LmZvcmVjYXN0LCBjb2wgPSAncmVkJykKYGBgCgpNU0UKYGBge3J9CnN1bSgodGFpbCh1aywgNzIpIC0gKHVrLnRyZW5kLmZvcmVjYXN0ICsgdWsuc2Vhc29uYWxpdHkuZm9yZWNhc3QpKV4yKS83MgpgYGAKCiMj0JTQvtCy0LXRgNC40YLQtdC70YzQvdGL0LUg0LjQvdGC0LXRgNCy0LDQu9GLCgpCb290c3RyYXA6CgoqINCS0YvQtNC10LvRj9C10Lwg0YHQuNCz0L3QsNC7INGBINC/0L7QvNC+0YnRjNGOIFNTQQoqINCe0YbQtdC90LjQstCw0LXQvCDQtNC40YHQv9C10YDRgdC40Y4g0L7RgdGC0LDRgtC60LAg0Lgg0LzQvdC+0LPQviDRgNCw0Lcg0LzQvtC00LXQu9C40YDRg9C10Lwg0LPQsNGD0YHRgdC+0LLRgdC60LjQuSDRiNGD0LwKKiDQktGL0LTQtdC70Y/QtdC8INGB0LjQs9C90LDQuyDQuNC3INC+0YbQtdC90LXQvdC90L7Qs9C+INGB0LjQs9C90LDQu9CwICsg0LzQvtC00LXQu9C40YDQvtCy0LDQvdC90L7Qs9C+INGI0YPQvNCwCiog0J/QvtC70YPRh9C40Lwg0LzQvdC+0LPQviDQvtGG0LXQvdC+0Log0YHQuNCz0L3QsNC70LAKKiDQodGC0YDQvtC40Lwg0L/RgNC+0LTQvtC70LbQtdC90LjRjwoqINCe0YLQsdGA0LDRgdGL0LLQsNC10LwgJCgxIC0gXGdhbW1hKS8yJCDRgSDQutC+0L3RhtC+0LIKCmBgYHtyfQp1ay50cmVuZC5mb3JlY2FzdCA8LSBwcmVkaWN0KHVrLnRyYWluLnNzYSwgbWV0aG9kID0gImJvb3RzdHJhcC1yZWN1cnJlbnQiLCBsZW4gPSA3MiwgZ3JvdXBzID0gMSkKdWsuc2Vhc29uYWxpdHkuZm9yZWNhc3QgPC0gcHJlZGljdCh1ay50cmFpbi5kZXRyZW5kZWQuc3NhLCBtZXRob2QgPSAiYm9vdHN0cmFwLXJlY3VycmVudCIsIGxlbiA9IDcyLCBncm91cHMgPSBsaXN0KDE6OSkpCnBsb3QodWssIHhsaW0gPSBjKDIwMTAsIDIwMTgpKQpsaW5lcyh1ay50cmVuZC5mb3JlY2FzdFssMV0gKyB1ay5zZWFzb25hbGl0eS5mb3JlY2FzdFssMV0sIGNvbCA9ICdyZWQnKQpsaW5lcyh1ay50cmVuZC5mb3JlY2FzdFssMl0gKyB1ay5zZWFzb25hbGl0eS5mb3JlY2FzdFssMl0sIGNvbCA9ICdibHVlJyxsdHk9ImRhc2hlZCIpCmxpbmVzKHVrLnRyZW5kLmZvcmVjYXN0WywzXSArIHVrLnNlYXNvbmFsaXR5LmZvcmVjYXN0WywzXSwgY29sID0gJ2JsdWUnLGx0eT0iZGFzaGVkIikKYGBgCgoj0JfQsNC/0L7Qu9C90LXQvdC40LUg0L/RgNC+0L/Rg9GB0LrQvtCyCgrQoNCw0YHRgdC80L7RgtGA0LjQvCDRgNGP0LQKYGBge3J9CnVrLmhlYWQgPC0gaGVhZCh1aywgLSAxMjApCnBsb3QodWsuaGVhZCkKYGBgCgoK0JTQvtCx0LDQstC40Lwg0L/RgNC+0L/Rg9GB0LrQuCDQsiDRgNGP0LQKYGBge3J9CnVrLm5hIDwtIHVrLmhlYWQKbWlzc2VkIDwtIHVrLmhlYWRbMTIxOjE4MF0KdWsubmFbMTIxOjE4MF0gPSBOQQpwbG90KHVrLm5hLCB0eXBlID0gJ2wnKQpgYGAKCiMj0JDQu9Cz0L7RgNC40YLQvAoKU3Vic3Bhc2UtYmFzZWQgbWV0aG9kOgoKKiDQndCwINGN0YLQsNC/0LUg0LTQtdC60L7QvNC/0L7Qt9C40YbQuNC4LCDQuNGB0LrQu9GO0YfQsNC10Lwg0LLQtdC60YLQvtGA0LAg0LLQu9C+0LbQtdC90LjRjywg0YHQvtC00LXRgNC20LDRidC40LUg0L/RgNC+0L/Rg9GJ0LXQvdC90YvQtSDRjdC70LXQvNC10L3RgtGLCiog0JjQtyDQvtGB0YLQsNCy0YjQuNGF0YHRjyDQstC10LrRgtC+0YDQvtCyINGB0L7RgdGC0LDQstC70Y/QtdC8INGC0YDQsNC10LrRgtC+0YDQvdGD0Y4g0LzQsNGC0YDQuNGG0YMKKiBTVkQKKiDQn9GA0L7QtdC60YbQuNGPINC90LAgJFxtYXRoY2Fse0x9X3IkIAoqINCf0YDQvtC10LrRhtC40Y8g0L3QtdC/0L7Qu9C90YvRhSDQstC10LrRgtC+0YDQvtCyINC90LAgJFxtYXRoY2Fse0x9X3IkLiAKKiAi0L/RgNC10LTRgdC60LDQt9Cw0L3QuNC1INCy0L4g0LLQvdGD0YLRgNGMIiDRgSDQv9C+0LzQvtGJ0YzRjiDQu9GA0YQsCtC/0L7RgdGC0YDQvtC10L3QvdC+0Lkg0L/QviDQuNC80LXRjtGJ0LjQvNGB0Y8g0LTQsNC90L3Ri9C8LiDQn9GA0LXQtNGB0LrQsNC30YvQstCw0LXQvCDRgdC70LXQstCwINC4INGB0L/RgNCw0LLQsCDRgSDQu9C40L3QtdC50L3QviDRg9Cx0YvQstCw0Y7RidC40LzQuCDQstC10YHQsNC80LgsCtCwINC/0L7RgtC+0Lwg0YPRgdGA0LXQtNC90Y/QtdC8Ciog0JTQuNCw0LPQvtC90LDQu9GM0L3QvtC1INGD0YHRgNC10LTQvdC10L3QuNC1CgrQmNGC0LXRgNCw0YLQuNCy0L3Ri9C5INC80LXRgtC+0LQ6IAoKKiDQl9Cw0L/QvtC70L3Rj9C10Lwg0L/RgNC+0L/Rg9GB0LrQuCwg0L3QsNC/0YDQuNC80LXRgCwg0YHRgNC10LTQvdC40LwKKiByINC4IEwg0YTQuNC60YHQuNGA0L7QstCw0L3RiyAo0L3QsNC/0YDQuNC80LXRgCDQstGL0LHRgNCw0LvQuCDRgSDQv9C+0LzQvtGJ0YzRjiBjdikKKiDQn9GA0LjQvNC10L3QuNC8IFNTQSwg0LLRgdGC0LDQstC40Lwg0LjQvdGC0LXRgNC10YHRg9GO0YnRg9GOINC90LDRgSDRh9Cw0YHRgtGMINCyINC40YHRhdC+0LTQvdGL0Lkg0YDRj9C0LCDRgdC90L7QstCwINC/0YDQuNC80LXQvdC40LwgU1NBINC4INGC0LDQuiDQtNCw0LvQtdC1CgrQnNC40L3Rg9GBOiDQtNC70Y8g0L/RgNC+0LPQvdC+0LfQsCDQvdC1INCz0L7QtNC40YLRgdGPLgoKYGBge3J9CnVrLm5hLnNzYSA8LSBzc2EodWsubmEsIEwgPSA3MikKYGBgCgojI9Cf0YDQtdC00YHQutCw0LfQsNC90LjQtSDQstC+INCy0L3Rg9GC0YDRjApgYGB7cn0KZyA8LSBnYXBmaWxsKHVrLm5hLnNzYSwgZ3JvdXBzID0gbGlzdCgxOjgpKQpwbG90KHVuY2xhc3ModWsuaGVhZCksIHR5cGUgPSAnbCcpCmxpbmVzKHkgPSB1bmNsYXNzKGdbMTIxOjE4MF0pLCB4ID0gMTIxOjE4MCwgY29sID0gJ3JlZCcsIGx0eSA9IDIpCmBgYApNU0UKYGBge3J9CnN1bSgoZ1sxMjE6MTgwXSAtIG1pc3NlZCleMikvNjAKYGBgCgojI9CY0YLQtdGA0LDRgtC40LLQvdGL0Lkg0LzQtdGC0L7QtAoKCmBgYHtyfQpnLml0IDwtIGlnYXBmaWxsKHVrLm5hLnNzYSwgZ3JvdXBzID0gbGlzdCgxOjgpKQpwbG90KHVuY2xhc3ModWsuaGVhZCksIHR5cGUgPSAnbCcpCmxpbmVzKHkgPSB1bmNsYXNzKGcuaXRbMTIxOjE4MF0pLCB4ID0gMTIxOjE4MCwgY29sID0gJ3JlZCcsIGx0eSA9IDIpCmBgYAoKTVNFCmBgYHtyfQpzdW0oKGcuaXRbMTIxOjE4MF0gLSBtaXNzZWQpXjIpLzYwCmBgYArQmNGC0LXRgNCw0YLQuNCy0L3Ri9C5INC80LXRgtC+0LQg0L7QutCw0LfQsNC70YHRjyDRgtC+0YfQvdC10LUuCgoKIzJELVNTQSDQtNC70Y8g0YDQsNC30LvQvtC20LXQvdC40Y8g0LjQt9C+0LHRgNCw0LbQtdC90LjRjwoK0KDQsNGB0YHQvNC+0YLRgNC40Lwg0YTQvtGC0L7Qs9GA0LDRhNC40Y4g0YHQv9GD0YLQvdC40LrQsCDQodCw0YLRg9GA0L3QsCwg0JTQuNC+0L3RiyAoW9CY0YHRgtC+0YfQvdC40LpdKGh0dHBzOi8vd3d3Lm5hc2EuZ292L2ltYWdlLWZlYXR1cmUvanBsL3BpYTIwNTIxL3JheXMtb2YtY3JldXNhKSkuCgrQn9GA0L7Rh9C40YLQsNC70Lgg0LrQsNGA0YLQuNC90LrRgwpgYGB7ciwgZmlnLndpZHRoPTUsIGZpZy5oZWlnaHQ9NX0KZGlvbmUgPC0gcmVhZEpQRUcoIkRpb25lLmpwZyIpCmltYWdlKGRpb25lLCBjb2wgPSBncmV5KHNlcSgwLCAxLCBsZW5ndGggPSAyNTYpKSkKYGBgCgrQlNC+0LHQsNCy0LjQvCDRiNGD0LwKYGBge3IsIGZpZy53aWR0aD01LCBmaWcuaGVpZ2h0PTV9CnNldC5zZWVkKDEwMCkKbm9pc2UubWF0cml4IDwtIGFwcGx5KGRpb25lLCAyLCBmdW5jdGlvbih4KXtybm9ybShsZW5ndGgoeCksIHNkID0gMC4zKX0pCmRpb25lLm5vaXNlZCA8LSBkaW9uZSArIG5vaXNlLm1hdHJpeAppbWFnZShkaW9uZS5ub2lzZWQsIGNvbCA9IGdyZXkoc2VxKDAsIDEsIGxlbmd0aCA9IDI1NikpKQpgYGAKCgoyZC1zc2EKYGBge3J9CmRpb25lLnNzYSA8LSBzc2EoZGlvbmUubm9pc2VkLCBraW5kID0gIjJkLXNzYSIsIEwgPSBjKDI1LDI1KSkKYGBgCgrQodC+0LHRgdGC0LLQtdC90L3Ri9C1INCy0LXQutGC0L7RgNCwCmBgYHtyfQpwbG90KGRpb25lLnNzYSwgdHlwZSA9ICJ2ZWN0b3JzIiwgaWR4ID0gMTozMCwgY3V0cyA9IDI1NSwgbGF5b3V0ID0gYygxMCwgMiksIHBsb3QuY29udHJpYiA9IEZBTFNFKQpgYGAKCnct0LrQvtGA0YDQtdC70Y/RhtC40L7QvdC90LDRjyDQvNCw0YLRgNC40YbQsApgYGB7cn0KcGxvdCh3Y29yKGRpb25lLnNzYSwgZ3JvdXBzID0gMTozMCksc2NhbGVzID0gbGlzdChhdCA9IHNlcSgxMCwgMzAsIDUpKSkKYGBgCgoK0KHQs9GA0YPQv9C/0LjRgNGD0LXQvCDQutC+0LzQv9C+0L3QtdC90YLRiyDRgSDRh9Cw0YHRgtC+0YLQsNC80Lgg0L/QvtGF0L7QttC10LPQviDQstC40LTQsApgYGB7cn0KZGlvbmUucmVjb25zdHJ1Y3RlZCA8LSByZWNvbnN0cnVjdChkaW9uZS5zc2EsIGdyb3VwcyA9IGxpc3QoSTEgPSBjKDE6MTMpLCBJMiA9IGMoMTQ6MTUpLCBJMyA9IGMoMTY6MTcpKSkKYGBgCgoK0J3QsNC60L7Qv9C70LXQvdC90YvQtSDRgdGD0LzQvNGLINC60L7QvNC/0L7QvdC10L3RggpgYGB7cn0KcGxvdChkaW9uZS5yZWNvbnN0cnVjdGVkLCBjdXRzID0gMjU1LCBsYXlvdXQgPSBjKDUsIDEpLCBwYXIuc3RyaXAudGV4dCA9IGxpc3QoY2V4ID0gMC43NSksIHR5cGUgPSAiY3Vtc3VtIiwgYXQgPSAiZnJlZSIpCmBgYAoKIyNTaGFwZWQgU1NBCgrQo9Cx0LXRgNC10Lwg0YfQtdGA0L3Ri9C5INGE0L7QvQpgYGB7cn0KZGlvbmUud2l0aG91dC5ibGFjayA8LSBhcHBseShkaW9uZSwgMiwgRlVOID0gZnVuY3Rpb24oY29sKXsKICBzYXBwbHkoY29sLCBGVU4gPSBmdW5jdGlvbihlbGVtZW50KXsKICAgIGlmKGVsZW1lbnQgPD0gMC4xKXsKICAgICAgcmV0dXJuKE5BKQogICAgfSBlbHNlewogICAgICByZXR1cm4oZWxlbWVudCkKICAgIH0KICB9KQp9KQpgYGAKCmBgYHtyLCBmaWcud2lkdGg9NSwgZmlnLmhlaWdodD01fQppbWFnZShkaW9uZS53aXRob3V0LmJsYWNrLCBjb2wgPSBncmV5KHNlcSgwLCAxLCBsZW5ndGggPSAyNTYpKSkKYGBgCtCU0L7QsdCw0LLQuNC8INGI0YPQvApgYGB7ciwgZmlnLndpZHRoPTUsIGZpZy5oZWlnaHQ9NX0KZGlvbmUud2l0aG91dC5ibGFjay5ub2lzZWQgPC0gZGlvbmUud2l0aG91dC5ibGFjayArIG5vaXNlLm1hdHJpeAppbWFnZShkaW9uZS53aXRob3V0LmJsYWNrLm5vaXNlZCwgY29sID0gZ3JleShzZXEoMCwgMSwgbGVuZ3RoID0gMjU2KSkpCmBgYAoK0J/QvtC/0YDQvtCx0YPQtdC8INC40LfQvNC10L3QuNGC0Ywg0YTQvtGA0LzRgyDQvtC60L3QsCDQvdCwINC60YDRg9CzLgpgYGB7cn0KZGlvbmUuc3NhLnNoYXBlZCA8LSBzc2EoZGlvbmUud2l0aG91dC5ibGFjay5ub2lzZWQsIGtpbmQgPSAiMmQtc3NhIiwgd21hc2sgPSBjaXJjbGUoMjApKQpgYGAKCmBgYHtyfQpwbG90KGRpb25lLnNzYS5zaGFwZWQsIHR5cGUgPSAidmVjdG9ycyIsIGlkeCA9IDE6MzAsIGN1dHMgPSAyNTUsIGxheW91dCA9IGMoMTAsIDIpLCBwbG90LmNvbnRyaWIgPSBGQUxTRSkKYGBgCgp3LdC60L7RgNGA0LXQu9GP0YbQuNC+0L3QvdCw0Y8g0LzQsNGC0YDQuNGG0LAKYGBge3J9CnBsb3Qod2NvcihkaW9uZS5zc2Euc2hhcGVkLCBncm91cHMgPSAxOjMwKSxzY2FsZXMgPSBsaXN0KGF0ID0gc2VxKDEwLCAzMCwgNSkpKQpgYGAK0J/QviDQvNCw0YLRgNC40YbQtSDQvNC+0LbQvdC+INGB0LTQtdC70LDRgtGMINCy0YvQstC+0LQg0L4g0YLQvtC8LCDRh9GC0L4g0L/RgNC40LHQu9C40LfQuNGC0LXQu9GM0L3QviAxMCDQv9C10YDQstGL0YUg0LrQvtC80L/QvtC90LXQvdGCINC+0YLQvdC+0YHRj9GC0YHRjyDQuiDRgdC40LPQvdCw0LvRgywg0LAg0L7RgdGC0LDQu9GM0L3QvtC1INGN0YLQviDRiNGD0LwuCgrQodCz0YDRg9C/0L/QuNGA0YPQtdC8INC60L7QvNC/0L7QvdC10L3RgtGLINGBINGH0LDRgdGC0L7RgtCw0LzQuCDQv9C+0YXQvtC20LXQs9C+INCy0LjQtNCwCmBgYHtyfQpkaW9uZS5yZWNvbnN0cnVjdGVkLnNoYXBlZCA8LSByZWNvbnN0cnVjdChkaW9uZS5zc2Euc2hhcGVkLCBncm91cHMgPSBsaXN0KEkxID0gYygxOjYpLCBJMiA9IGMoNzoxMiksIEkzID0gYygxMzoxOSkpKQpgYGAKCgrQndCw0LrQvtC/0LvQtdC90L3Ri9C1INGB0YPQvNC80Ysg0LrQvtC80L/QvtC90LXQvdGCCmBgYHtyfQpwbG90KGRpb25lLnJlY29uc3RydWN0ZWQuc2hhcGVkLCBjdXRzID0gMjU1LCBsYXlvdXQgPSBjKDUsIDEpLCBwYXIuc3RyaXAudGV4dCA9IGxpc3QoY2V4ID0gMC43NSksIHR5cGUgPSAiY3Vtc3VtIiwgYXQgPSAiZnJlZSIpCmBgYArQndCwINCy0LjQtCwg0LjRgdGF0L7QtNC90LDRjyDQutCw0YDRgtC40L3QutCwINC70YPRh9GI0LUg0L7RgtC00LXQu9C40LvQsNGB0Ywg0L7RgiDRiNGD0LzQsCDQsiDRgdC70YPRh9Cw0LUgc2hhcGVkIFNTQS4KCgojRXhwb25lbnRpYWwgU21vb3RoaW5nCgooRVMgdnMgU1NBIHZzIEFSSU1BKQoKYGBge3J9CmVzLmZpdCA8LSBlcyh1ay50cmFpbiwgaCA9IDcyKQpgYGAKCk1TRQpgYGB7cn0Kc3VtKCh0YWlsKHVrLDcyKSAtIGVzLmZpdCRmb3JlY2FzdCleMikvNzIKYGBgCgrQnNC+0LTQtdC70YwKYGBge3J9CmVzLmZpdCRmb3JtdWxhCmBgYAoKCmBgYHtyfQp1ay50cmVuZC5mb3JlY2FzdCA8LSBwcmVkaWN0KHVrLnRyYWluLnNzYSwgbWV0aG9kID0gInJlY3VycmVudCIsIGxlbiA9IDcyLCBncm91cHMgPSAxKQp1ay5zZWFzb25hbGl0eS5mb3JlY2FzdCA8LSBwcmVkaWN0KHVrLnRyYWluLmRldHJlbmRlZC5zc2EsIG1ldGhvZCA9ICJyZWN1cnJlbnQiLCBsZW4gPSA3MiwgZ3JvdXBzID0gbGlzdCgxOjkpKQpwbG90KHVrLCB4bGltID0gYygyMDEwLCAyMDE4KSkKbGluZXModWsudHJlbmQuZm9yZWNhc3QgKyB1ay5zZWFzb25hbGl0eS5mb3JlY2FzdCwgY29sID0gJ3JlZCcpCmBgYApgYGB7cn0Kc3VtKCh0YWlsKHVrLDcyKSAtICh1ay50cmVuZC5mb3JlY2FzdCArIHVrLnNlYXNvbmFsaXR5LmZvcmVjYXN0KSleMikvNzIKYGBgCgoKYGBge3J9CmF1dG8uZml0LnVrIDwtIGF1dG8uYXJpbWEodWsudHJhaW4sIHRyYWNlID0gVFJVRSwgc3RlcHdpc2UgPSBGQUxTRSwgYWxsb3dkcmlmdCA9IEZBTFNFKQphcmltYS5wcmVkaWN0aW9uLnVrIDwtIHByZWRpY3QoYXV0by5maXQudWssIDcyKQpwbG90KHVrLCB4bGltID0gYygyMDEwLCAyMDE4KSkKbGluZXMoYXJpbWEucHJlZGljdGlvbi51ayRwcmVkLCBjb2wgPSAncmVkJykKbGluZXMoYXJpbWEucHJlZGljdGlvbi51ayRwcmVkICsgYXJpbWEucHJlZGljdGlvbi51ayRzZSwgbHR5ID0gJ2Rhc2hlZCcsIGNvbCA9ICdibHVlJykKbGluZXMoYXJpbWEucHJlZGljdGlvbi51ayRwcmVkIC0gYXJpbWEucHJlZGljdGlvbi51ayRzZSwgbHR5ID0gJ2Rhc2hlZCcsIGNvbCA9ICdibHVlJykKYGBgCgpNU0UKYGBge3J9CnN1bSgodGFpbCh1ayw3MikgLSBhcmltYS5wcmVkaWN0aW9uLnVrJHByZWQpXjIpLzcyCmBgYAoK